Index: channels/chan_zap.c
===================================================================
--- channels/chan_zap.c	(revision 8223)
+++ channels/chan_zap.c	(working copy)
@@ -96,6 +96,11 @@
 #include "asterisk/term.h"
 #include "asterisk/utils.h"
 #include "asterisk/transcap.h"
+#ifdef WITH_SMDI
+#include "asterisk/smdi.h"
+#include "asterisk/astobj.h"
+#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
+#endif
 
 #ifndef ZT_SIG_EM_E1
 #error "Your zaptel is too old.  please cvs update"
@@ -270,7 +275,10 @@
 static int amaflags = 0;
 
 static int adsi = 0;
-
+#ifdef WITH_SMDI
+static int use_smdi = 0;
+static char smdi_port[SMDI_MAX_FILENAME_LEN] = "/dev/ttyS0";
+#endif
 static int numbufs = 4;
 
 static int cur_prewink = -1;
@@ -596,6 +604,10 @@
 	unsigned int r2blocked:1;
 	unsigned int sigchecked:1;
 #endif
+#ifdef WITH_SMDI
+	unsigned int use_smdi:1;		/* Whether to use SMDI on this channel */
+	struct ast_smdi_interface *smdi_iface;	/* The serial port to listen for SMDI data on */
+#endif
 
 	struct zt_distRings drings;
 
@@ -2149,6 +2161,10 @@
 		p->prev->next = p->next;
 	if(p->next)
 		p->next->prev = p->prev;
+#ifdef WITH_SMDI
+	if(p->use_smdi)
+		ASTOBJ_UNREF(p->smdi_iface, ast_smdi_interface_destroy);
+#endif
 	ast_mutex_destroy(&p->lock);
 	free(p);
 	*pvt = NULL;
@@ -5185,7 +5201,9 @@
 	int counter1;
 	int counter;
 	int samples = 0;
-
+#ifdef WITH_SMDI
+	struct ast_smdi_md_message *smdi_msg = NULL;
+#endif
 	int flags;
 	int i;
 	int timeout;
@@ -5787,10 +5805,35 @@
 			}
 		}
 #endif
+#ifdef WITH_SMDI
+		/* check for SMDI messages */
+		if(p->use_smdi && p->smdi_iface) {
+			smdi_msg = ast_smdi_md_message_wait(p->smdi_iface, SMDI_MD_WAIT_TIMEOUT);
+
+			if(smdi_msg != NULL) {
+				ast_copy_string(chan->exten, smdi_msg->fwd_st, sizeof(chan->exten));
+
+				if (smdi_msg->type == 'B')
+					pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "b");
+				else if (smdi_msg->type == 'N')
+					pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "u");
+
+				ast_log(LOG_DEBUG, "Recieved SMDI message on %s\n", chan->name);
+			} else {
+				ast_log(LOG_WARNING, "SMDI enabled but no SMDI message present\n");
+			}
+		}
+
+		if (p->use_callerid && (p->cid_signalling == CID_SIG_SMDI && smdi_msg)) {
+	     		number = smdi_msg->calling_st;
+
 		/* If we want caller id, we're in a prering state due to a polarity reversal
 		 * and we're set to use a polarity reversal to trigger the start of caller id,
 		 * grab the caller id and wait for ringing to start... */
+		} else if (p->use_callerid && (chan->_state == AST_STATE_PRERING && p->cid_start == CID_START_POLARITY)) {
+#else
 		if (p->use_callerid && (chan->_state == AST_STATE_PRERING && p->cid_start == CID_START_POLARITY)) {
+#endif
 			/* If set to use DTMF CID signalling, listen for DTMF */
 			if (p->cid_signalling == CID_SIG_DTMF) {
 				int i = 0;
@@ -6132,7 +6175,10 @@
 			ast_shrink_phone_number(number);
 
 		ast_set_callerid(chan, number, name, number);
-
+#ifdef WITH_SMDI
+		if (smdi_msg)
+			ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy);
+#endif
 		if (cs)
 			callerid_free(cs);
 		ast_setstate(chan, AST_STATE_RING);
@@ -7109,6 +7155,9 @@
 		tmp->callwaitingcallerid = callwaitingcallerid;
 		tmp->threewaycalling = threewaycalling;
 		tmp->adsi = adsi;
+#ifdef WITH_SMDI
+		tmp->use_smdi = use_smdi;
+#endif
 		tmp->permhidecallerid = hidecallerid;
 		tmp->callreturn = callreturn;
 		tmp->echocancel = echocancel;
@@ -7141,6 +7190,22 @@
 			}
 		}
 
+#ifdef WITH_SMDI
+		if (tmp->cid_signalling == CID_SIG_SMDI) {
+			if (!tmp->use_smdi) {
+				ast_log(LOG_WARNING, "SMDI callerid requires SMDI to be enabled, enabling...\n");
+				tmp->use_smdi = 1;
+			}
+		}
+		if (tmp->use_smdi) {
+			tmp->smdi_iface = ast_smdi_interface_find(smdi_port);
+			if (!(tmp->smdi_iface)) {
+				ast_log(LOG_ERROR, "Invalid SMDI port specfied, disabling SMDI support\n");
+				tmp->use_smdi = 0;
+			}
+		}
+#endif
+
 		ast_copy_string(tmp->accountcode, accountcode, sizeof(tmp->accountcode));
 		tmp->amaflags = amaflags;
 		if (!here) {
@@ -10292,6 +10357,10 @@
 				cid_signalling = CID_SIG_V23;
 			else if (!strcasecmp(v->value, "dtmf"))
 				cid_signalling = CID_SIG_DTMF;
+#ifdef WITH_SMDI
+			else if (!strcasecmp(v->value, "smdi"))
+				cid_signalling = CID_SIG_SMDI;
+#endif
 			else if (ast_true(v->value))
 				cid_signalling = CID_SIG_BELL;
 		} else if (!strcasecmp(v->name, "cidstart")) {
@@ -10314,6 +10383,12 @@
 			ast_copy_string(mailbox, v->value, sizeof(mailbox));
 		} else if (!strcasecmp(v->name, "adsi")) {
 			adsi = ast_true(v->value);
+#ifdef WITH_SMDI
+		} else if (!strcasecmp(v->name, "usesmdi")) {
+			use_smdi = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "smdiport")) {
+			ast_copy_string(smdi_port, v->value, sizeof(smdi_port));
+#endif
 		} else if (!strcasecmp(v->name, "transfer")) {
 			transfer = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "canpark")) {
Index: channels/Makefile
===================================================================
--- channels/Makefile	(revision 8223)
+++ channels/Makefile	(working copy)
@@ -77,6 +77,13 @@
   CHANNEL_LIBS+=chan_phone.so
 endif
 
+#
+# Asterisk SMDI integration
+#
+ifeq (${WITH_SMDI},1)
+CFLAGS+=-DWITH_SMDI
+endif
+
 ifneq ($(wildcard h323/libchanh323.a),)
   CHANNEL_LIBS+=chan_h323.so
 endif
Index: apps/Makefile
===================================================================
--- apps/Makefile	(revision 8223)
+++ apps/Makefile	(working copy)
@@ -32,6 +32,13 @@
      app_mixmonitor.so app_stack.so
 
 #
+# Asterisk SMDI integration
+#
+ifeq (${WITH_SMDI},1)
+CFLAGS+=-DWITH_SMDI
+endif
+
+#
 # Obsolete things...
 #
 #APPS+=app_sql_postgres.so
Index: apps/app_voicemail.c
===================================================================
--- apps/app_voicemail.c	(revision 8223)
+++ apps/app_voicemail.c	(working copy)
@@ -70,6 +70,10 @@
 #include "asterisk/localtime.h"
 #include "asterisk/cli.h"
 #include "asterisk/utils.h"
+#ifdef WITH_SMDI
+#include "asterisk/smdi.h"
+#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
+#endif
 #ifdef USE_ODBC_STORAGE
 #include "asterisk/res_odbc.h"
 #endif
@@ -382,7 +386,9 @@
 static char serveremail[80];
 static char mailcmd[160];	/* Configurable mail cmd */
 static char externnotify[160]; 
-
+#ifdef WITH_SMDI
+static struct ast_smdi_interface *smdi_iface = NULL;
+#endif
 static char vmfmts[80];
 static int vmminmessage;
 static int vmmaxmessage;
@@ -2316,13 +2322,34 @@
 	char arguments[255];
 	char ext_context[256] = "";
 	int newvoicemails = 0, oldvoicemails = 0;
+#ifdef WITH_SMDI
+	struct ast_smdi_mwi_message *mwi_msg;
+#endif
 
 	if (!ast_strlen_zero(context))
 		snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
 	else
 		ast_copy_string(ext_context, extension, sizeof(ext_context));
 
+#ifdef WITH_SMDI
+	if (!strcasecmp(externnotify, "smdi")) {
+
+		if(ast_app_has_voicemail(ext_context, NULL)) 
+			ast_smdi_mwi_set(smdi_iface, extension);
+		else
+			ast_smdi_mwi_unset(smdi_iface, extension);
+
+		mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT);
+		if(mwi_msg) {
+			ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+			ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+		} else {
+			ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+		}
+	} else if (!ast_strlen_zero(externnotify)) {
+#else
 	if (!ast_strlen_zero(externnotify)) {
+#endif
 		if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
 			ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
 		} else {
@@ -5817,6 +5844,9 @@
 	char *cat;
 	struct ast_variable *var;
 	char *notifystr = NULL;
+#ifdef WITH_SMDI
+	char *smdistr = NULL;
+#endif
 	char *astattach;
 	char *astsearch;
 	char *astsaycid;
@@ -5926,6 +5956,25 @@
 		if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
 			ast_copy_string(externnotify, notifystr, sizeof(externnotify));
 			ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
+#ifdef WITH_SMDI
+			if(!strcasecmp(externnotify, "smdi")) {
+				ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
+
+				if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
+					smdi_iface = ast_smdi_interface_find(smdistr);
+				} else {
+					ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
+					smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
+				}
+
+				if(!smdi_iface) {
+					ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
+					externnotify[0] = '\0';
+				} else {
+					ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name);
+				}
+			}
+#endif
 		} else {
 			externnotify[0] = '\0';
 		}
Index: Makefile
===================================================================
--- Makefile	(revision 8223)
+++ Makefile	(working copy)
@@ -68,6 +68,11 @@
 #OPTIONS += -DLOW_MEMORY
 #endif
 
+#
+# Asterisk SMDI integration
+#
+#WITH_SMDI = 1
+
 # Optional debugging parameters
 DEBUG_THREADS = #-DDUMP_SCHEDULER #-DDEBUG_SCHEDULER #-DDEBUG_THREADS #-DDO_CRASH #-DDETECT_DEADLOCKS
 
Index: include/asterisk/astobj.h
===================================================================
--- include/asterisk/astobj.h	(revision 8223)
+++ include/asterisk/astobj.h	(working copy)
@@ -571,6 +571,76 @@
 		found; \
 	})
 
+/*! \brief Add an object to the end of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ *
+ * This macro adds an object to the end of a container.
+ */
+#define ASTOBJ_CONTAINER_LINK_END(container,newobj) \
+	do { \
+		typeof((container)->head) iterator; \
+		typeof((container)->head) next; \
+		typeof((container)->head) prev; \
+		ASTOBJ_CONTAINER_RDLOCK(container); \
+		prev = NULL; \
+		next = (container)->head; \
+		while((iterator = next)) { \
+			next = iterator->next[0]; \
+			prev = iterator; \
+		} \
+		if(prev) { \
+			ASTOBJ_CONTAINER_WRLOCK((container)); \
+			prev->next[0] = ASTOBJ_REF(newobj); \
+			(newobj)->next[0] = NULL; \
+			ASTOBJ_CONTAINER_UNLOCK((container)); \
+		} else { \
+			ASTOBJ_CONTAINER_LINK_START((container),(newobj)); \
+		} \
+		ASTOBJ_CONTAINER_UNLOCK((container)); \
+	} while(0)
+
+/*! \brief Add an object to the front of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ * \param newobj A pointer to the object to be added.
+ *
+ * This macro adds an object to the start of a container.
+ */
+#define ASTOBJ_CONTAINER_LINK_START(container,newobj) \
+	do { \
+		ASTOBJ_CONTAINER_WRLOCK(container); \
+		(newobj)->next[0] = (container)->head; \
+		(container)->head = ASTOBJ_REF(newobj); \
+		ASTOBJ_CONTAINER_UNLOCK(container); \
+	} while(0)
+
+/*! \brief Remove an object from the front of a container.
+ *
+ * \param container A pointer to the container to operate on.
+ *
+ * This macro removes the first object in a container.
+ *
+ * \note This macro does not destroy any objects, it simply unlinks
+ * them from the list.  No destructors are called.
+ *
+ * \return The container's reference to the removed object or NULL if no
+ * matching object was found.
+ */
+#define ASTOBJ_CONTAINER_UNLINK_START(container) \
+	({ \
+		typeof((container)->head) found = NULL; \
+		ASTOBJ_CONTAINER_WRLOCK(container); \
+	 	if((container)->head) { \
+	 		found = (container)->head; \
+	 		(container)->head = (container)->head->next[0]; \
+	 		found->next[0] = NULL; \
+	 	} \
+		ASTOBJ_CONTAINER_UNLOCK(container); \
+	 	found; \
+	})
+
 /*! \brief Prune marked objects from a container.
  *
  * \param container A pointer to the container to prune.
Index: include/asterisk/callerid.h
===================================================================
--- include/asterisk/callerid.h	(revision 8223)
+++ include/asterisk/callerid.h	(working copy)
@@ -41,6 +41,9 @@
 #define CID_SIG_BELL	1
 #define CID_SIG_V23	2
 #define CID_SIG_DTMF	3
+#ifdef WITH_SMDI
+#define CID_SIG_SMDI	4
+#endif
 
 #define CID_START_RING	1
 #define CID_START_POLARITY 2
Index: configs/voicemail.conf.sample
===================================================================
--- configs/voicemail.conf.sample	(revision 8223)
+++ configs/voicemail.conf.sample	(working copy)
@@ -49,12 +49,18 @@
 silencethreshold=128
 ; Max number of failed login attempts
 maxlogins=3
-; If you need to have an external program, i.e. /usr/bin/myapp called when a
-; voicemail is left, delivered, or your voicemailbox is checked, uncomment
-; this:
+
+; If you need to have an external program, i.e. /usr/bin/myapp
+; called when a voicemail is left, delivered, or your voicemailbox 
+; is checked, uncomment this.  It can also be set to 'smdi' to use
+; smdi for external notification.  If it is 'smdi', smdiport should
+; be set to a valid port as specfied in smdi.conf.
+
 ;externnotify=/usr/bin/myapp
-; If you need to have an external program, i.e. /usr/bin/myapp called when a
-; voicemail password is changed, uncomment this:
+;smdiport=/dev/ttyS0
+
+; If you need to have an external program, i.e. /usr/bin/myapp
+; called when a voicemail password is changed, uncomment this:
 ;externpass=/usr/bin/myapp
 ; For the directory, you can override the intro file if you want
 ;directoryintro=dir-intro
Index: configs/zapata.conf.sample
===================================================================
--- configs/zapata.conf.sample	(revision 8223)
+++ configs/zapata.conf.sample	(working copy)
@@ -222,6 +222,7 @@
 ;     bell     = bell202 as used in US
 ;     v23      = v23 as used in the UK
 ;     dtmf     = DTMF as used in Denmark, Sweden and Netherlands
+;     smdi     = Use SMDI for callerid.  Requires SMDI to be enabled (usesmdi).
 ;
 ;cidsignalling=bell
 ;
@@ -376,6 +377,14 @@
 ;
 ;adsi=yes
 ;
+; SMDI (Simplified Message Desk Interface) can be enabled on a per-channel
+; basis if you would like that channel to behave like an SMDI message desk.
+; The SMDI port specfied should have already been defined in smdi.conf.  The
+; default port is /dev/ttyS0.
+;
+;usesmdi=yes
+;smdiport=/dev/ttyS0
+;
 ; On trunk interfaces (FXS) and E&M interfaces (E&M, Wink, Feature Group D
 ; etc, it can be useful to perform busy detection either in an effort to 
 ; detect hangup or for detecting busies.  This enables listening for
Index: res/Makefile
===================================================================
--- res/Makefile	(revision 8223)
+++ res/Makefile	(working copy)
@@ -69,6 +69,13 @@
 CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC
 endif
 
+#
+# Asterisk SMDI integration
+#
+ifeq (${WITH_SMDI},1)
+MODS+=res_smdi.so
+endif
+
 all: depend $(MODS)
 
 install: all
