[svn-commits] russell: branch group/upenn r100834 - in /team/group/upenn: apps/ channels/ c...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Jan 29 10:05:11 CST 2008


Author: russell
Date: Tue Jan 29 10:05:11 2008
New Revision: 100834

URL: http://svn.digium.com/view/asterisk?view=rev&rev=100834
Log:
Merge in changes from team/russell/smdi-1.4

Modified:
    team/group/upenn/apps/app_voicemail.c
    team/group/upenn/channels/chan_zap.c
    team/group/upenn/configs/smdi.conf.sample
    team/group/upenn/include/asterisk/smdi.h
    team/group/upenn/res/res_smdi.c

Modified: team/group/upenn/apps/app_voicemail.c
URL: http://svn.digium.com/view/asterisk/team/group/upenn/apps/app_voicemail.c?view=diff&rev=100834&r1=100833&r2=100834
==============================================================================
--- team/group/upenn/apps/app_voicemail.c (original)
+++ team/group/upenn/apps/app_voicemail.c Tue Jan 29 10:05:11 2008
@@ -2869,8 +2869,8 @@
 		else
 			ast_smdi_mwi_unset(smdi_iface, extension);
 
-		if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
-			ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+		if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
+			ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
 			if (!strncmp(mwi_msg->cause, "INV", 3))
 				ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
 			else if (!strncmp(mwi_msg->cause, "BLK", 3))
@@ -2879,7 +2879,7 @@
 			ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
 		} else {
 			if (option_debug)
-				ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+				ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
 		}
 	} else if (!ast_strlen_zero(externnotify)) {
 		if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
@@ -7635,9 +7635,6 @@
 				if (!smdi_iface) {
 					ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
 					externnotify[0] = '\0';
-				} else {
-					if (option_debug > 2)
-						ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name);
 				}
 			}
 		} else {

Modified: team/group/upenn/channels/chan_zap.c
URL: http://svn.digium.com/view/asterisk/team/group/upenn/channels/chan_zap.c?view=diff&rev=100834&r1=100833&r2=100834
==============================================================================
--- team/group/upenn/channels/chan_zap.c (original)
+++ team/group/upenn/channels/chan_zap.c Tue Jan 29 10:05:11 2008
@@ -2219,7 +2219,7 @@
 	if (p->next)
 		p->next->prev = p->prev;
 	if (p->use_smdi)
-		ASTOBJ_UNREF(p->smdi_iface, ast_smdi_interface_destroy);
+		ast_smdi_interface_unref(p->smdi_iface);
 	ast_mutex_destroy(&p->lock);
 	free(p);
 	*pvt = NULL;

Modified: team/group/upenn/configs/smdi.conf.sample
URL: http://svn.digium.com/view/asterisk/team/group/upenn/configs/smdi.conf.sample?view=diff&rev=100834&r1=100833&r2=100834
==============================================================================
--- team/group/upenn/configs/smdi.conf.sample (original)
+++ team/group/upenn/configs/smdi.conf.sample Tue Jan 29 10:05:11 2008
@@ -41,3 +41,35 @@
 ;msgexpirytime = 30000
 
 ;smdiport => /dev/ttyS0
+
+
+[mailboxes]
+; This section configures parameters related to MWI handling for the SMDI link.
+
+; This option configures the polling interval used to check to see if the
+; mailboxes have any new messages.  This option is specified in seconds.
+; The default value is 10 seconds.
+;
+;pollinginterval=10
+
+; Every other entry in this section of the configuration file is interpreted as
+; a mapping between the mailbox ID on the SMDI link, and the local Asterisk
+; mailbox name.  In many cases, they are the same thing, but they still must be
+; listed here so that this module knows which mailboxes it needs to pay
+; attention to.
+;
+; Syntax:
+;   <SMDI mailbox ID>=<Asterisk Mailbox Name>[@Asterisk Voicemail Context]
+;
+; If no Asterisk voicemail context is specified, "default" will be assumed.
+;
+; Before specifying mailboxes, you must specify an SMDI interface.  All mailbox
+; definitions that follow will correspond to that SMDI interface.  If you specify
+; another interface, then all definitions following that will correspond to the
+; new interface.
+;
+;smdiport=/dev/ttyS0
+;2565551234=1234 at vmcontext1
+;2565555678=5678 at vmcontext2
+;smdiport=/dev/ttyS1
+;2565559999=9999

Modified: team/group/upenn/include/asterisk/smdi.h
URL: http://svn.digium.com/view/asterisk/team/group/upenn/include/asterisk/smdi.h?view=diff&rev=100834&r1=100833&r2=100834
==============================================================================
--- team/group/upenn/include/asterisk/smdi.h (original)
+++ team/group/upenn/include/asterisk/smdi.h Tue Jan 29 10:05:11 2008
@@ -73,16 +73,6 @@
 	struct timeval timestamp;				/* a timestamp for the message */
 };
 
-/*! \brief SMDI message desk message queue. */
-struct ast_smdi_md_queue {
-	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
-};
-
-/*! \brief SMDI message waiting indicator message queue. */
-struct ast_smdi_mwi_queue {
-	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
-};
-
 /*! 
  * \brief SMDI interface structure.
  *
@@ -90,38 +80,114 @@
  * should be monitored for SMDI activity.  The structure contains a message
  * queue of messages that have been recieved on the interface.
  */
-struct ast_smdi_interface {
-	ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
-	struct ast_smdi_md_queue md_q;
-	struct ast_smdi_mwi_queue mwi_q;
-	FILE *file;
-	int fd;
-	pthread_t thread;
-	struct termios mode;
-	int msdstrip;
-	long msg_expiry;
-};
+struct ast_smdi_interface;
 
+void ast_smdi_interface_unref(struct ast_smdi_interface *iface);
 
-/* MD message queue functions */
+/*! 
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ *
+ * This function pulls the first unexpired message from the SMDI message queue
+ * on the specified interface.  It will purge all expired SMDI messages before
+ * returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages.
+ */
 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface);
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ * \param timeout the time to wait before returning in milliseconds.
+ *
+ * This function pulls a message from the SMDI message queue on the specified
+ * interface.  If no message is available this function will wait the specified
+ * amount of time before returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages and
+ * the timeout has expired.
+ */
 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout);
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param md_msg a pointer to the message to use.
+ *
+ * This function puts a message back in the front of the specified queue.  It
+ * should be used if a message was popped but is not going to be processed for
+ * some reason, and the message needs to be returned to the queue.
+ */
 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
 
-/* MWI message queue functions */
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ *
+ * This function pulls the first unexpired message from the SMDI message queue
+ * on the specified interface.  It will purge all expired SMDI messages before
+ * returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages.
+ */
 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface);
+
+/*!
+ * \brief Get the next SMDI message from the queue.
+ * \param iface a pointer to the interface to use.
+ * \param timeout the time to wait before returning in milliseconds.
+ *
+ * This function pulls a message from the SMDI message queue on the specified
+ * interface.  If no message is available this function will wait the specified
+ * amount of time before returning.
+ *
+ * \return the next SMDI message, or NULL if there were no pending messages and
+ * the timeout has expired.
+ */
 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout);
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, 
+	int timeout, const char *station);
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param mwi_msg a pointer to the message to use.
+ *
+ * This function puts a message back in the front of the specified queue.  It
+ * should be used if a message was popped but is not going to be processed for
+ * some reason, and the message needs to be returned to the queue.
+ */
 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
 
+/*!
+ * \brief Find an SMDI interface with the specified name.
+ * \param iface_name the name/port of the interface to search for.
+ *
+ * \return a pointer to the interface located or NULL if none was found.  This
+ * actually returns an ASTOBJ reference and should be released using
+ * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
+ */
 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name);
 
-/* MWI functions */
+/*!
+ * \brief Set the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox);
+
+/*! 
+ * \brief Unset the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox);
 
+/*! \brief ast_smdi_md_message destructor. */
 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg);
+
+/*! \brief ast_smdi_mwi_message destructor. */
 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg);
 
-void ast_smdi_interface_destroy(struct ast_smdi_interface *iface);
-
 #endif /* !ASTERISK_SMDI_H */

Modified: team/group/upenn/res/res_smdi.c
URL: http://svn.digium.com/view/asterisk/team/group/upenn/res/res_smdi.c?view=diff&rev=100834&r1=100833&r2=100834
==============================================================================
--- team/group/upenn/res/res_smdi.c (original)
+++ team/group/upenn/res/res_smdi.c Tue Jan 29 10:05:11 2008
@@ -1,9 +1,10 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005-2007, Digium, Inc.
  *
  * Matthew A. Nicholson <mnicholson at digium.com>
+ * Russell Bryant <russell at digium.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -20,6 +21,10 @@
  * \file
  * \brief SMDI support for Asterisk.
  * \author Matthew A. Nicholson <mnicholson at digium.com>
+ * \author Russell Bryant <russell at digium.com>
+ *
+ * Here is a useful mailing list post that describes SMDI protocol details:
+ * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
  */
 
 #include "asterisk.h"
@@ -44,24 +49,118 @@
 #include "asterisk/logger.h"
 #include "asterisk/utils.h"
 #include "asterisk/options.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
 
 /* Message expiry time in milliseconds */
 #define SMDI_MSG_EXPIRY_TIME	30000 /* 30 seconds */
 
 static const char config_file[] = "smdi.conf";
 
-static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
-static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
-
-static void *smdi_read(void *iface_p);
-static int smdi_load(int reload);
-
-struct module_symbols *me;	/* initialized in load_module() */
+/*! \brief SMDI message desk message queue. */
+struct ast_smdi_md_queue {
+	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
+};
+
+/*! \brief SMDI message waiting indicator message queue. */
+struct ast_smdi_mwi_queue {
+	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
+};
+
+struct ast_smdi_interface {
+	ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
+	struct ast_smdi_md_queue md_q;
+	ast_mutex_t md_q_lock;
+	ast_cond_t md_q_cond;
+	struct ast_smdi_mwi_queue mwi_q;
+	ast_mutex_t mwi_q_lock;
+	ast_cond_t mwi_q_cond;
+	FILE *file;
+	int fd;
+	pthread_t thread;
+	struct termios mode;
+	int msdstrip;
+	long msg_expiry;
+};
 
 /*! \brief SMDI interface container. */
 struct ast_smdi_interface_container {
 	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
 } smdi_ifaces;
+
+/*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
+struct mailbox_mapping {
+	/*! This is the current state of the mailbox.  It is simply on or
+	 *  off to indicate if there are messages waiting or not. */
+	unsigned int cur_state:1;
+	/*! A Pointer to the appropriate SMDI interface */
+	struct ast_smdi_interface *iface;
+	AST_DECLARE_STRING_FIELDS(
+		/*! The Name of the mailbox for the SMDI link. */
+		AST_STRING_FIELD(smdi);
+		/*! The name of the mailbox on the Asterisk side */
+		AST_STRING_FIELD(mailbox);
+		/*! The name of the voicemail context in use */
+		AST_STRING_FIELD(context);
+	);
+	AST_LIST_ENTRY(mailbox_mapping) entry;
+};
+
+/*! 10 seconds */
+#define DEFAULT_POLLING_INTERVAL 10
+
+/*! \brief Data that gets used by the SMDI MWI monitoring thread */
+static struct {
+	/*! The thread ID */
+	pthread_t thread;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	/*! A list of mailboxes that need to be monitored */
+	AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
+	/*! Polling Interval for checking mailbox status */
+	unsigned int polling_interval;
+	/*! Set to 1 to tell the polling thread to stop */
+	unsigned int stop:1;
+	/*! The time that the last poll began */
+	struct timeval last_poll;
+} mwi_monitor = {
+	.thread = AST_PTHREADT_NULL,
+};
+
+static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
+{
+	if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
+		pthread_cancel(iface->thread);
+		pthread_join(iface->thread, NULL);
+	}
+	
+	iface->thread = AST_PTHREADT_STOP;
+	
+	if (iface->file) 
+		fclose(iface->file);
+	
+	ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
+	ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
+	ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
+	ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
+
+	ast_mutex_destroy(&iface->md_q_lock);
+	ast_cond_destroy(&iface->md_q_cond);
+
+	ast_mutex_destroy(&iface->mwi_q_lock);
+	ast_cond_destroy(&iface->mwi_q_cond);
+
+	free(iface);
+
+	ast_module_unref(ast_module_info->self);
+}
+
+void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
+{
+	ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+}
 
 /*! 
  * \internal
@@ -71,7 +170,10 @@
  */
 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
 {
+	ast_mutex_lock(&iface->md_q_lock);
 	ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
+	ast_cond_broadcast(&iface->md_q_cond);
+	ast_mutex_unlock(&iface->md_q_lock);
 }
 
 /*!
@@ -82,249 +184,288 @@
  */
 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
 {
+	ast_mutex_lock(&iface->mwi_q_lock);
 	ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
-}
-
-/*!
- * \brief Set the MWI indicator for a mailbox.
- * \param iface the interface to use.
- * \param mailbox the mailbox to use.
- */
-int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+	ast_cond_broadcast(&iface->md_q_cond);
+	ast_mutex_unlock(&iface->mwi_q_lock);
+}
+
+static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
 {
 	FILE *file;
 	int i;
 	
-	file = fopen(iface->name, "w");
-	if(!file) {
+	if (!(file = fopen(iface->name, "w"))) {
 		ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
 		return 1;
 	}	
 
 	ASTOBJ_WRLOCK(iface);
 
-	fprintf(file, "OP:MWI ");
-
-	for(i = 0; i < iface->msdstrip; i++)
+	fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
+
+	for (i = 0; i < iface->msdstrip; i++)
 	   fprintf(file, "0");
 
 	fprintf(file, "%s!\x04", mailbox);
+
 	fclose(file);
 
 	ASTOBJ_UNLOCK(iface);
-	ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
+
+	ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset", 
+		mailbox, iface->name);
+
 	return 0;
 }
 
-/*! 
- * \brief Unset the MWI indicator for a mailbox.
- * \param iface the interface to use.
- * \param mailbox the mailbox to use.
- */
+int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+{
+	return smdi_toggle_mwi(iface, mailbox, 1);
+}
+
 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
 {
-	FILE *file;
-	int i;
+	return smdi_toggle_mwi(iface, mailbox, 0);
+}
+
+void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
+{
+	ast_mutex_lock(&iface->md_q_lock);
+	ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
+	ast_cond_broadcast(&iface->md_q_cond);
+	ast_mutex_unlock(&iface->md_q_lock);
+}
+
+void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
+{
+	ast_mutex_lock(&iface->mwi_q_lock);
+	ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
+	ast_cond_broadcast(&iface->mwi_q_cond);
+	ast_mutex_unlock(&iface->mwi_q_lock);
+}
+
+enum smdi_message_type {
+	SMDI_MWI,
+	SMDI_MD,
+};
+
+static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ast_mutex_lock(&iface->mwi_q_lock);
+	case SMDI_MD:	
+		return ast_mutex_lock(&iface->md_q_lock);
+	}
 	
-	file = fopen(iface->name, "w");
-	if(!file) {
-		ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
-		return 1;
-	}	
-
-	ASTOBJ_WRLOCK(iface);
-
-	fprintf(file, "RMV:MWI ");
-
-	for(i = 0; i < iface->msdstrip; i++)
-	   fprintf(file, "0");
-
-	fprintf(file, "%s!\x04", mailbox);
-	fclose(file);
-
-	ASTOBJ_UNLOCK(iface);
-	ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s\n", mailbox, iface->name);
-	return 0;
-}
-
-/*!
- * \brief Put an SMDI message back in the front of the queue.
- * \param iface a pointer to the interface to use.
- * \param md_msg a pointer to the message to use.
- *
- * This function puts a message back in the front of the specified queue.  It
- * should be used if a message was popped but is not going to be processed for
- * some reason, and the message needs to be returned to the queue.
- */
-void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
-{
-	ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
-}
-
-/*!
- * \brief Put an SMDI message back in the front of the queue.
- * \param iface a pointer to the interface to use.
- * \param mwi_msg a pointer to the message to use.
- *
- * This function puts a message back in the front of the specified queue.  It
- * should be used if a message was popped but is not going to be processed for
- * some reason, and the message needs to be returned to the queue.
- */
-void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
-{
-	ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
-}
-
-/*! 
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- *
- * This function pulls the first unexpired message from the SMDI message queue
- * on the specified interface.  It will purge all expired SMDI messages before
- * returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages.
- */
-struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
-{
-	struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+	return -1;
+}
+
+static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ast_mutex_unlock(&iface->mwi_q_lock);
+	case SMDI_MD:
+		return ast_mutex_unlock(&iface->md_q_lock);
+	}
+
+	return -1;
+}
+
+static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
+	case SMDI_MD:
+		return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+	}
+
+	return NULL;
+}
+
+static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
+{
+	struct ast_smdi_md_message *md_msg = msg;
+	struct ast_smdi_mwi_message *mwi_msg = msg;
+
+	switch (type) {
+	case SMDI_MWI:
+		return mwi_msg->timestamp;
+	case SMDI_MD:
+		return md_msg->timestamp;
+	}
+
+	return ast_tv(0, 0);
+}
+
+static inline void unref_msg(void *msg, enum smdi_message_type type)
+{
+	struct ast_smdi_md_message *md_msg = msg;
+	struct ast_smdi_mwi_message *mwi_msg = msg;
+
+	switch (type) {
+	case SMDI_MWI:
+		ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+	case SMDI_MD:
+		ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+	}
+}
+
+static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
 	struct timeval now;
 	long elapsed = 0;
+	void *msg;
+	
+	lock_msg_q(iface, type);
+	msg = unlink_from_msg_q(iface, type);
+	unlock_msg_q(iface, type);
 
 	/* purge old messages */
 	now = ast_tvnow();
-	while (md_msg) {
-		elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
+	while (msg) {
+		elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
 
 		if (elapsed > iface->msg_expiry) {
 			/* found an expired message */
-			ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
-			ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue.  Message was %ld milliseconds too old.\n",
-				iface->name, elapsed - iface->msg_expiry);
-			md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
-		}
-		else {
-			/* good message, return it */
+			unref_msg(msg, type);
+			ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
+				"Message was %ld milliseconds too old.\n",
+				iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
+				elapsed - iface->msg_expiry);
+
+			lock_msg_q(iface, type);
+			msg = unlink_from_msg_q(iface, type);
+			unlock_msg_q(iface, type);
+		} else {
+			/* good message, put it back and return */
+			switch (type) {
+			case SMDI_MD:
+				ast_smdi_md_message_push(iface, msg);
+				break;
+			case SMDI_MWI:
+				ast_smdi_mwi_message_push(iface, msg);
+				break;
+			}
+			unref_msg(msg, type);
 			break;
 		}
 	}
-
-	return md_msg;
-}
-
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- * \param timeout the time to wait before returning in milliseconds.
- *
- * This function pulls a message from the SMDI message queue on the specified
- * interface.  If no message is available this function will wait the specified
- * amount of time before returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages and
- * the timeout has expired.
- */
-extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+}
+
+static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	void *msg;
+
+	purge_old_messages(iface, type);
+
+	lock_msg_q(iface, type);
+	msg = unlink_from_msg_q(iface, type);
+	unlock_msg_q(iface, type);
+
+	return msg;
+}
+
+static void *smdi_msg_find(struct ast_smdi_interface *iface,
+	enum smdi_message_type type, const char *station)
+{
+	void *msg = NULL;
+
+	purge_old_messages(iface, type);
+
+	switch (type) {
+	case SMDI_MD:
+		msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, station);
+		break;
+	case SMDI_MWI:
+		msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, station);
+		break;
+	}
+
+	return msg;
+}
+
+static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
+	enum smdi_message_type type, const char *station)
 {
 	struct timeval start;
 	long diff = 0;
-	struct ast_smdi_md_message *msg;
+	void *msg;
 
 	start = ast_tvnow();
 	while (diff < timeout) {
-
-		if ((msg = ast_smdi_md_message_pop(iface)))
+		struct timespec ts = { 0, };
+		struct timeval tv;
+
+		lock_msg_q(iface, type);
+
+		if ((msg = smdi_msg_find(iface, type, station))) {
+			unlock_msg_q(iface, type);
 			return msg;
+		}
+
+		tv = ast_tvadd(start, ast_tv(0, timeout));
+		ts.tv_sec = tv.tv_sec;
+		ts.tv_nsec = tv.tv_usec * 1000;
+
+		/* If there were no messages in the queue, then go to sleep until one
+		 * arrives. */
+
+		ast_cond_wait(&iface->md_q_cond, &iface->md_q_lock);
+
+		if ((msg = smdi_msg_find(iface, type, station))) {
+			unlock_msg_q(iface, type);
+			return msg;
+		}
+
+		unlock_msg_q(iface, type);
 
 		/* check timeout */
 		diff = ast_tvdiff_ms(ast_tvnow(), start);
 	}
 
-	return (ast_smdi_md_message_pop(iface));
-}
-
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- *
- * This function pulls the first unexpired message from the SMDI message queue
- * on the specified interface.  It will purge all expired SMDI messages before
- * returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages.
- */
-extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
-{
-	struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
-	struct timeval now;
-	long elapsed = 0;
-
-	/* purge old messages */
-	now = ast_tvnow();
-	while (mwi_msg)	{
-		elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
-
-		if (elapsed > iface->msg_expiry) {
-			/* found an expired message */
-			ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
-			ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue.  Message was %ld milliseconds too old.\n",
-				iface->name, elapsed - iface->msg_expiry);
-			mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
-		}
-		else {
-			/* good message, return it */
-			break;
-		}
-	}
-
-	return mwi_msg;
-}
-
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- * \param timeout the time to wait before returning in milliseconds.
- *
- * This function pulls a message from the SMDI message queue on the specified
- * interface.  If no message is available this function will wait the specified
- * amount of time before returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages and
- * the timeout has expired.
- */
-extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
-{
-	struct timeval start;
-	long diff = 0;
-	struct ast_smdi_mwi_message *msg;
-
-	start = ast_tvnow();
-	while (diff < timeout) {
-
-		if ((msg = ast_smdi_mwi_message_pop(iface)))
-			return msg;
-
-		/* check timeout */
-		diff = ast_tvdiff_ms(ast_tvnow(), start);
-	}
-
-	return (ast_smdi_mwi_message_pop(iface));
-}
-
-/*!
- * \brief Find an SMDI interface with the specified name.
- * \param iface_name the name/port of the interface to search for.
- *
- * \return a pointer to the interface located or NULL if none was found.  This
- * actually returns an ASTOBJ reference and should be released using
- * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
- */
-extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
+	/* A timeout occurred, but try one last time ... */
+
+	return smdi_msg_pop(iface, type);
+}
+
+struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
+{
+	return smdi_msg_pop(iface, SMDI_MD);
+}
+
+struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MD, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
+{
+	return smdi_msg_pop(iface, SMDI_MWI);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MWI, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
+	const char *station)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MWI, station);
+}
+
+struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
 {
 	return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
 }
 
-/*! \brief Read an SMDI message.
+/*! 
+ * \internal
+ * \brief Read an SMDI message.
  *
  * \param iface_p the SMDI interface to read from.
  *
@@ -348,120 +489,128 @@
 		if (!start) {
 			if (c == 'M')
 				start = 1;
+			continue;
 		}
-		else { /* Determine if this is a MD or MWI message */
-			if(c == 'D') { /* MD message */
-				start = 0;
-
-				if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
-					ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
-					return NULL;
+		
+		if (c == 'D') { /* MD message */
+			start = 0;
+
+			if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+				return NULL;
+			}
+			
+			ASTOBJ_INIT(md_msg);
+
+			/* read the message desk number */
+			for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++)
+				md_msg->mesg_desk_num[i] = fgetc(iface->file);
+
+			md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
+
+			/* read the message desk terminal number */
+			for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++)
+				md_msg->mesg_desk_term[i] = fgetc(iface->file);
+
+			md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
+
+			/* read the message type */
+			md_msg->type = fgetc(iface->file);
+		   
+			/* read the forwarding station number (may be blank) */
+			cp = &md_msg->fwd_st[0];
+			for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
+				if ((c = fgetc(iface->file)) == ' ') {
+					*cp = '\0';
+					break;
 				}
-				
-				ASTOBJ_INIT(md_msg);
-
-				/* read the message desk number */
-				for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++)
-					md_msg->mesg_desk_num[i] = fgetc(iface->file);
-
-				md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
-
-				/* read the message desk terminal number */
-				for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++)
-					md_msg->mesg_desk_term[i] = fgetc(iface->file);
-
-				md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
-
-				/* read the message type */
-				md_msg->type = fgetc(iface->file);
-			   
-				/* read the forwarding station number (may be blank) */
-				cp = &md_msg->fwd_st[0];
-				for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
-					if((c = fgetc(iface->file)) == ' ') {
-						*cp = '\0';
-						break;
-					}
-
-					/* store c in md_msg->fwd_st */
-					if( i >= iface->msdstrip)
-						*cp++ = c;
+
+				/* store c in md_msg->fwd_st */
+				if (i >= iface->msdstrip)
+					*cp++ = c;
+			}
+
+			/* make sure the value is null terminated, even if this truncates it */
+			md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
+			cp = NULL;
+		
+			/* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+			 * up a message on this field */
+			ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
+
+			/* read the calling station number (may be blank) */
+			cp = &md_msg->calling_st[0];
+			for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
+				if (!isdigit((c = fgetc(iface->file)))) {
+					*cp = '\0';
+					break;
 				}
 
-				/* make sure the value is null terminated, even if this truncates it */
-				md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
-				cp = NULL;
-				
-				/* read the calling station number (may be blank) */
-				cp = &md_msg->calling_st[0];
-				for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
-					if (!isdigit((c = fgetc(iface->file)))) {
-						*cp = '\0';
-						break;
-					}
-
-					/* store c in md_msg->calling_st */
-					if (i >= iface->msdstrip)
-						*cp++ = c;
+				/* store c in md_msg->calling_st */
+				if (i >= iface->msdstrip)
+					*cp++ = c;
+			}
+
+			/* make sure the value is null terminated, even if this truncates it */
+			md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
+			cp = NULL;
+
+			/* add the message to the message queue */
+			md_msg->timestamp = ast_tvnow();
+			ast_smdi_md_message_push(iface, md_msg);
+			ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
+			
+			ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+
+		} else if (c == 'W') { /* MWI message */
+			start = 0;
+
+			if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+				ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+				return NULL;
+			}
+
+			ASTOBJ_INIT(mwi_msg);
+
+			/* discard the 'I' (from 'MWI') */
+			fgetc(iface->file);
+			
+			/* read the forwarding station number (may be blank) */
+			cp = &mwi_msg->fwd_st[0];
+			for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
+				if ((c = fgetc(iface->file)) == ' ') {
+					*cp = '\0';
+					break;
 				}
 
-				/* make sure the value is null terminated, even if this truncates it */
-				md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
-				cp = NULL;
-
-				/* add the message to the message queue */
-				md_msg->timestamp = ast_tvnow();
-				ast_smdi_md_message_push(iface, md_msg);
-				ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
-				
-				ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
-
-			} else if(c == 'W') { /* MWI message */
-				start = 0;
-
-				if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
-					ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
-					return NULL;
-				}
-
-				ASTOBJ_INIT(mwi_msg);
-
-				/* discard the 'I' (from 'MWI') */
-				fgetc(iface->file);
-				
-				/* read the forwarding station number (may be blank) */
-				cp = &mwi_msg->fwd_st[0];
-				for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
-					if ((c = fgetc(iface->file)) == ' ') {
-						*cp = '\0';
-						break;
-					}
-
-					/* store c in md_msg->fwd_st */
-					if (i >= iface->msdstrip)
-						*cp++ = c;
-				}
-
-				/* make sure the station number is null terminated, even if this will truncate it */
-				mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
-				cp = NULL;
-				
-				/* read the mwi failure cause */
-				for (i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++)
-					mwi_msg->cause[i] = fgetc(iface->file);
-
-				mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
-
-				/* add the message to the message queue */
-				mwi_msg->timestamp = ast_tvnow();
-				ast_smdi_mwi_message_push(iface, mwi_msg);
-				ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
-				
-				ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
-			} else {
-				ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
-				start = 0;
-			}
+				/* store c in md_msg->fwd_st */
+				if (i >= iface->msdstrip)
+					*cp++ = c;
+			}
+
+			/* make sure the station number is null terminated, even if this will truncate it */
+			mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
+			cp = NULL;
+			
+			/* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+			 * up a message on this field */
+			ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
+
+			/* read the mwi failure cause */
+			for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
+				mwi_msg->cause[i] = fgetc(iface->file);
+
+			mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
+
+			/* add the message to the message queue */
+			mwi_msg->timestamp = ast_tvnow();
+			ast_smdi_mwi_message_push(iface, mwi_msg);
+			ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
+			
+			ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+		} else {
+			ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
+			start = 0;
 		}
 	}
 
@@ -470,38 +619,130 @@
 	return NULL;
 }
 
-/*! \brief ast_smdi_md_message destructor. */
 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
 {
 	free(msg);
 }
 
-/*! \brief ast_smdi_mwi_message destructor. */
 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
 {
 	free(msg);
 }
 
-/*! \brief ast_smdi_interface destructor. */
-void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
-{
-	if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
-		pthread_cancel(iface->thread);
-		pthread_join(iface->thread, NULL);
-	}
+static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
+{
+	ast_string_field_free_memory(mm);
+	ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
+	free(mm);
+}
+
+static void destroy_all_mailbox_mappings(void)
+{
+	struct mailbox_mapping *mm;
+
+	ast_mutex_lock(&mwi_monitor.lock);
+	while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
+		destroy_mailbox_mapping(mm);
+	ast_mutex_unlock(&mwi_monitor.lock);
+}
+
+static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
+{
+	struct mailbox_mapping *mm;
+	char *mailbox, *context;
+
+	if (!(mm = ast_calloc(1, sizeof(*mm))))
+		return;
 	
-	iface->thread = AST_PTHREADT_STOP;
-	
-	if(iface->file) 
-		fclose(iface->file);
-	
-	ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
-	ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
-	ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
-	ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
-	free(iface);
-
-	ast_module_unref(ast_module_info->self);
+	if (ast_string_field_init(mm, 32)) {
+		free(mm);
+		return;
+	}
+
+	ast_string_field_set(mm, smdi, var->name);
+
+	context = ast_strdupa(var->value);
+	mailbox = strsep(&context, "@");
+	if (ast_strlen_zero(context))
+		context = "default";
+
+	ast_string_field_set(mm, mailbox, mailbox);
+	ast_string_field_set(mm, context, context);
+
+	mm->iface = ASTOBJ_REF(iface);
+
+	ast_mutex_lock(&mwi_monitor.lock);
+	AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
+	ast_mutex_unlock(&mwi_monitor.lock);
+}
+
+/*!
+ * \note Called with the mwi_monitor.lock locked
+ */
+static void poll_mailbox(struct mailbox_mapping *mm)
+{
+	char buf[1024];
+	unsigned int state;
+
+	snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
+
+	state = !!ast_app_has_voicemail(mm->mailbox, NULL);
+
+	if (state != mm->cur_state) {
+		if (state)
+			ast_smdi_mwi_set(mm->iface, mm->smdi);
+		else
+			ast_smdi_mwi_unset(mm->iface, mm->smdi);
+
+		mm->cur_state = state;
+	}
+}
+
+static void *mwi_monitor_handler(void *data)
+{
+	while (!mwi_monitor.stop) {
+		struct timespec ts = { 0, };
+		struct timeval tv;
+		struct mailbox_mapping *mm;
+
+		ast_mutex_lock(&mwi_monitor.lock);
+
+		mwi_monitor.last_poll = ast_tvnow();
+
+		AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
+			poll_mailbox(mm);
+
+		/* Sleep up to the configured polling interval.  Allow unload_module()
+		 * to signal us to wake up and exit. */
+		tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
+		ts.tv_sec = tv.tv_sec;
+		ts.tv_nsec = tv.tv_usec * 1000;
+		ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
+
+		ast_mutex_unlock(&mwi_monitor.lock);
+	}
+
+	return NULL;
+}
+
+static struct ast_smdi_interface *alloc_smdi_interface(void)
+{
+	struct ast_smdi_interface *iface;
+
+	if (!(iface = ast_calloc(1, sizeof(*iface))))
+		return NULL;
+
+	ASTOBJ_INIT(iface);

[... 376 lines stripped ...]



More information about the svn-commits mailing list