diff --git a/CHANGES b/CHANGES
index 1c7e5c982f728022c79a5aca8f7aca6a170195db..71e325bd71817c5d48f4cfe34cd24113c439e898 100644
--- a/CHANGES
+++ b/CHANGES
@@ -11,6 +11,8 @@
 --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3  -------------
 ------------------------------------------------------------------------------
 
+
+
 SIP Changes
 -----------
  * Added preferred_codec_only option in sip.conf. This feature limits the joint
@@ -142,6 +144,7 @@ DAHDI Changes
    and a 'full' buffer policy for a fax transmission, add:
      faxbuffers=>6,full
    The faxbuffers configuration will be in affect until the call is torn down.
+ * Added service message support for 4ess/5ess switches.
 
 Dialplan Functions
 ------------------
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index f6d3fb9ad8b06fc3ab3fa7f6348d871713fd8d36..adc7e510a03478ae197e345dfc6d569df9510011 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -292,6 +292,28 @@ static const char config[] = "chan_dahdi.conf";
 #define CALLPROGRESS_FAX_INCOMING	4
 #define CALLPROGRESS_FAX		(CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING)
 
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+/*! \brief Persistent Service State */
+#define SRVST_DBKEY "service-state"
+/*! \brief The out-of-service SERVICE state */
+#define SRVST_TYPE_OOS "O"
+/*! \brief SRVST_INITIALIZED is used to indicate a channel being out-of-service 
+ *  The SRVST_INITIALIZED is mostly used maintain backwards compatibility but also may
+ *  mean that the channel has not yet received a RESTART message.  If a channel is
+ *  out-of-service with this reason a RESTART message will result in the channel
+ *  being put into service. */
+#define SRVST_INITIALIZED 0
+/*! \brief SRVST_NEAREND is used to indicate that the near end was put out-of-service */
+#define SRVST_NEAREND  (1 << 0)
+/*! \brief SRVST_FAREND is used to indicate that the far end was taken out-of-service */
+#define SRVST_FAREND   (1 << 1)
+/*! \brief SRVST_BOTH is used to indicate that both sides of the channel are out-of-service */
+#define SRVST_BOTH (SRVST_NEAREND | SRVST_FAREND)
+
+/*! \brief The AstDB family */
+static const char dahdi_db[] = "dahdi/registry";
+#endif
+
 static char defaultcic[64] = "";
 static char defaultozz[64] = "";
 
@@ -543,6 +565,9 @@ struct dahdi_pri {
 	int resetting;
 	/*! \brief Current position during a reset (-1 if not started) */
 	int resetpos;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+	unsigned int enable_service_message_support:1;	/*!< enable SERVICE message support */
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
 	unsigned int inbanddisconnect:1;				/*!< Should we support inband audio after receiving DISCONNECT? */
 #endif
@@ -3934,6 +3959,21 @@ static void destroy_all_channels(void)
 		pl = p;
 		p = p->next;
 		x = pl->channel;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+		{
+			char db_chan_name[20], db_answer[5], state;
+			int why = -1;
+
+			snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pl->span, x);
+			if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+				sscanf(db_answer, "%c:%d", &state, &why);
+			}
+			if (!why) {
+				/* SRVST persistence is not required */
+				ast_db_del(db_chan_name, SRVST_DBKEY);
+			}
+		}
+#endif
 		/* Free associated memory */
 		if (pl)
 			destroy_dahdi_pvt(&pl);
@@ -10134,7 +10174,6 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 							}
 						}
 					}
-					offset = p.chanpos;
 					if (!matchesdchan) {
 						if (pris[span].nodetype && (pris[span].nodetype != conf->pri.nodetype)) {
 							ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype));
@@ -10190,6 +10229,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 						pris[span].overlapdial = conf->pri.overlapdial;
 						pris[span].qsigchannelmapping = conf->pri.qsigchannelmapping;
 						pris[span].discardremoteholdretrieval = conf->pri.discardremoteholdretrieval;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+						pris[span].enable_service_message_support = conf->pri.enable_service_message_support;
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
 						pris[span].inbanddisconnect = conf->pri.inbanddisconnect;
 #endif
@@ -10204,7 +10246,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 						pris[span].resetinterval = conf->pri.resetinterval;
 
 						tmp->pri = &pris[span];
-						tmp->prioffset = offset;
+						if (si.spanno != span + 1) { /* in another trunkgroup */
+							tmp->prioffset = pris[span].numchans;
+						} else {
+							tmp->prioffset = p.chanpos;
+						}
 						tmp->call = NULL;
 					} else {
 						ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset);
@@ -10483,10 +10529,23 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 		tmp->sendcalleridafter = conf->chan.sendcalleridafter;
 		if (!here) {
 			tmp->locallyblocked = tmp->remotelyblocked = 0;
-			if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7))
+			if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7)) {
 				tmp->inservice = 0;
-			else /* We default to in service on protocols that don't have a reset */
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+				if (chan_sig == SIG_PRI) {
+					char db_chan_name[20], db_answer[5];
+
+					snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, tmp->channel);
+					if (ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+						snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_INITIALIZED);
+						ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+					}
+				}
+#endif
+			} else { 
+				 /* We default to in service on protocols that don't have a reset */
 				tmp->inservice = 1;
+			}
 		}
 	}
 	if (tmp && !here) {
@@ -10536,7 +10595,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 	return tmp;
 }
 
-static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched)
+static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *reason, int *channelmatched, int *groupmatched)
 {
 	int res;
 	struct dahdi_params par;
@@ -10554,9 +10613,9 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
 		*channelmatched = 1;
 	}
 	/* We're at least busy at this point */
-	if (busy) {
+	if (reason) {
 		if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS))
-			*busy = 1;
+			*reason = AST_CAUSE_BUSY;
 	}
 	/* If do not disturb, definitely not */
 	if (p->dnd)
@@ -10573,10 +10632,25 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
 #ifdef HAVE_PRI
 		/* Trust PRI */
 		if (p->pri) {
-			if (p->resetting || p->call)
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+			char db_chan_name[20], db_answer[5], state;
+			int why = 0;
+						
+			snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, p->span, p->channel);
+			if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+				sscanf(db_answer, "%c:%d", &state, &why);
+			}
+			if ((p->resetting || p->call) || (why)) {
+				if (why) {
+					*reason = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+				}
+#else
+			if (p->resetting || p->call) {
+#endif
 				return 0;
-			else
+			} else {
 				return 1;
+			}
 		}
 #endif
 #ifdef HAVE_SS7
@@ -10736,7 +10810,7 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
 	int channelmatch = -1;
 	int roundrobin = 0;
 	int callwait = 0;
-	int busy = 0;
+	int unavailreason = 0;
 	struct dahdi_pvt *p;
 	struct ast_channel *tmp = NULL;
 	char *dest=NULL;
@@ -10865,7 +10939,7 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
 		ast_verbose("name = %s, %d, %d, %d\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch);
 #endif
 
-		if (p && available(p, channelmatch, groupmatch, &busy, &channelmatched, &groupmatched)) {
+		if (p && available(p, channelmatch, groupmatch, &unavailreason, &channelmatched, &groupmatched)) {
 			ast_debug(1, "Using channel %d\n", p->channel);
 			if (p->inalarm)
 				goto next;
@@ -10973,10 +11047,10 @@ next:
 		*cause = AST_CAUSE_BUSY;
 	else if (!tmp) {
 		if (channelmatched) {
-			if (busy)
+			if (unavailreason)
 				*cause = AST_CAUSE_BUSY;
 		} else if (groupmatched) {
-			*cause = AST_CAUSE_CONGESTION;
+			*cause = (unavailreason) ? unavailreason : AST_CAUSE_CONGESTION;
 		}
 	}
 
@@ -12181,6 +12255,7 @@ static void dahdi_pri_error(struct pri *pri, char *s)
 #if defined(HAVE_PRI)
 static int pri_check_restart(struct dahdi_pri *pri)
 {
+tryanotherpos:
 	do {
 		pri->resetpos++;
 	} while ((pri->resetpos < pri->numchans) &&
@@ -12188,6 +12263,24 @@ static int pri_check_restart(struct dahdi_pri *pri)
 		pri->pvts[pri->resetpos]->call ||
 		pri->pvts[pri->resetpos]->resetting));
 	if (pri->resetpos < pri->numchans) {
+		char db_chan_name[20], db_answer[5], state;
+		int why;
+
+		/* check if the channel is out of service */
+		ast_mutex_lock(&pri->pvts[pri->resetpos]->lock);
+		snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[pri->resetpos]->span, pri->pvts[pri->resetpos]->channel);
+		ast_mutex_unlock(&pri->pvts[pri->resetpos]->lock);
+
+		/* if so, try next channel */
+		if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+			sscanf(db_answer, "%c:%d", &state, &why);
+			if (why) {
+				ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), not sending RESTART\n", pri->span,
+				pri->pvts[pri->resetpos]->channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end");
+				goto tryanotherpos;
+			}
+		}
+
 		/* Mark the channel as resetting and restart it */
 		pri->pvts[pri->resetpos]->resetting = 1;
 		pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos]));
@@ -12566,13 +12659,36 @@ static void *pri_dchannel(void *vpri)
 						ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n",
 							PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
 					else {
-						ast_verb(3, "B-channel %d/%d restarted on span %d\n",
-								PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+						char db_chan_name[20], db_answer[5], state;
+						int why, skipit = 0;
+						
 						ast_mutex_lock(&pri->pvts[chanpos]->lock);
-						if (pri->pvts[chanpos]->call) {
-							pri_destroycall(pri->pri, pri->pvts[chanpos]->call);
-							pri->pvts[chanpos]->call = NULL;
+						snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, pri->pvts[chanpos]->channel);
+						ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+
+						if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+							sscanf(db_answer, "%c:%d", &state, &why);
+							if (why) {
+								ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), ignoring RESTART\n", pri->span,
+									e->restart.channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end");
+								skipit = 1;
+							} else {
+								ast_db_del(db_chan_name, SRVST_DBKEY);
+							}
+						}
+						if (!skipit) {
+#endif
+							ast_verb(3, "B-channel %d/%d restarted on span %d\n",
+									PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span);
+							ast_mutex_lock(&pri->pvts[chanpos]->lock);
+							if (pri->pvts[chanpos]->call) {
+								pri_destroycall(pri->pri, pri->pvts[chanpos]->call);
+								pri->pvts[chanpos]->call = NULL;
+							}
+#ifdef HAVE_PRI_SERVICE_MESSAGES
 						}
+#endif
 						/* Force soft hangup if appropriate */
 						if (pri->pvts[chanpos]->realcall)
 							pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
@@ -12652,6 +12768,62 @@ static void *pri_dchannel(void *vpri)
 					}
 				}
 				break;
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+			case PRI_EVENT_SERVICE:
+				chanpos = pri_find_principle(pri, e->service.channel);
+				if (chanpos < 0) {
+					ast_log(LOG_WARNING, "Received service change status %d on unconfigured channel %d/%d span %d\n",
+						e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span);
+				} else {
+					char db_chan_name[20], db_answer[5], state;
+					int ch, why = -1;
+
+					ast_mutex_lock(&pri->pvts[chanpos]->lock);
+					ch = pri->pvts[chanpos]->channel;
+					ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+					
+					snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, ch);
+					if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+						sscanf(db_answer, "%c:%d", &state, &why);
+						ast_db_del(db_chan_name, SRVST_DBKEY);
+					}
+					switch (e->service.changestatus) {
+					case 0: /* in-service */
+						if (why > -1) {
+							if (why & SRVST_NEAREND) {
+								snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_NEAREND);
+								ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+								ast_debug(2, "channel '%d' service state { near: out-of-service,  far: in-service }\n", ch);
+							}
+						}
+						break;
+					case 2: /* out-of-service */
+						if (why == -1) {
+							why = SRVST_FAREND;
+						} else {
+							why |= SRVST_FAREND;
+						}
+						snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+						ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+						break;
+					default:
+						ast_log(LOG_ERROR, "Huh?  changestatus is: %d\n", e->service.changestatus);
+					}
+					ast_log(LOG_NOTICE, "Channel %d/%d span %d (logical: %d) received a change of service message, status '%d'\n",
+						PRI_SPAN(e->service.channel), PRI_CHANNEL(e->service.channel), pri->span, ch, e->service.changestatus);
+				}
+				break;
+			case PRI_EVENT_SERVICE_ACK:
+				chanpos = pri_find_principle(pri, e->service_ack.channel);
+				if (chanpos < 0) {
+					ast_log(LOG_WARNING, "Received service acknowledge change status '%d' on unconfigured channel %d/%d span %d\n",
+						e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span);
+				} else {
+					ast_debug(2, "Channel %d/%d span %d received a change os service acknowledgement message, status '%d'\n",
+						PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span, e->service_ack.changestatus);
+				}
+				break;
+#endif
 			case PRI_EVENT_RING:
 				crv = NULL;
 				if (e->ring.channel == -1)
@@ -13448,6 +13620,11 @@ static int start_pri(struct dahdi_pri *pri)
 			break;
 		default:
 			pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+				if (pri->enable_service_message_support) {
+					pri_set_service_message_support(pri->dchans[i], 1);
+				}
+#endif
 			break;
 		}
 		/* Force overlap dial if we're doing GR-303! */
@@ -13622,6 +13799,149 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 }
 #endif	/* defined(HAVE_PRI) */
 
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a, int changestatus)
+{
+	int why;
+	int channel;
+	int trunkgroup;
+	int x, y, fd = a->fd;
+	int interfaceid = 0;
+	char *c;
+	char state;
+	char db_chan_name[20], db_answer[5];
+	struct dahdi_pvt *start, *tmp = NULL;
+	struct dahdi_pri *pri = NULL;
+	ast_mutex_t *lock;
+	
+	lock = &iflock;
+	start = iflist;
+
+	if (a->argc < 5 || a->argc > 6)
+		return CLI_SHOWUSAGE;
+	if ((c = strchr(a->argv[4], ':'))) {
+		if (sscanf(a->argv[4], "%d:%d", &trunkgroup, &channel) != 2)
+			return CLI_SHOWUSAGE;
+		if ((trunkgroup < 1) || (channel < 1))
+			return CLI_SHOWUSAGE;
+		for (x=0;x<NUM_SPANS;x++) {
+			if (pris[x].trunkgroup == trunkgroup) {
+				pri = pris + x;
+				break;
+			}
+		}
+		if (pri) {
+			start = pri->crvs;
+			lock = &pri->lock;
+		} else {
+			ast_cli(fd, "No such trunk group %d\n", trunkgroup);
+			return CLI_FAILURE;
+		}
+	} else
+		channel = atoi(a->argv[4]);
+	
+	if (a->argc == 6)
+		interfaceid = atoi(a->argv[5]);
+
+	/* either servicing a D-Channel */
+	for (x = 0; x < NUM_SPANS; x++) {
+		for (y = 0; y < NUM_DCHANS; y++) {
+			if (pris[x].dchannels[y] == channel) {
+				pri = pris + x;
+				pri_maintenance_service(pri->pri, interfaceid, -1, changestatus);
+				return CLI_SUCCESS;
+			}
+		}
+	}
+
+	/* or servicing a B-Channel */
+	ast_mutex_lock(lock);
+	tmp = start;
+	while (tmp) {
+		if (tmp->pri && tmp->channel == channel) {
+			if (!tmp->pri->enable_service_message_support) {
+				ast_cli(fd, "\n\tThis operation has not been enabled in chan_dahdi.conf, set 'service_message_support=yes' to use this operation.\n\tNote only 4ess and 5ess switch types are supported.\n\n");
+				return CLI_SUCCESS;
+			}
+			why = -1;
+			snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, channel);
+			if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
+				sscanf(db_answer, "%c:%d", &state, &why);
+				ast_db_del(db_chan_name, SRVST_DBKEY);
+			}
+			switch(changestatus) {
+			case 0: /* enable */
+				if (why > -1) {
+					if (why & SRVST_FAREND) {
+						why = SRVST_FAREND;
+						snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+						ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+						ast_debug(2, "channel '%d' service state { near: in-service,  far: out-of-service }\n", channel);
+					}
+				}
+				break;
+			/* case 1:  -- loop */
+			case 2: /* disable */
+				if (why == -1) {
+					why = SRVST_NEAREND;
+				} else {
+					why |= SRVST_NEAREND;
+				}
+				snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why);
+				ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+				break;
+			/* case 3:  -- continuity */
+			/* case 4:  -- shutdown */
+			default:
+				ast_log(LOG_WARNING, "Unsupported changestatus: '%d'\n", changestatus);
+			}
+			pri_maintenance_service(tmp->pri->pri, PRI_SPAN(PVT_TO_CHANNEL(tmp)), PVT_TO_CHANNEL(tmp), changestatus);
+			ast_mutex_unlock(lock);
+			return CLI_SUCCESS;
+		}
+		tmp = tmp->next;
+	}
+	ast_mutex_unlock(lock);
+
+	ast_cli(fd, "Unable to find given channel %d, possibly not a PRI\n", channel);
+	return CLI_FAILURE;
+}
+
+static char *handle_pri_service_enable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch (cmd) {
+	case CLI_INIT:	
+		e->command = "pri service enable channel";
+		e->usage = 
+			"Usage: pri service enable channel <channel> [<interface id>]\n"
+			"       Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n"
+			"	to restore a channel to service, with optional interface id\n"
+			"	as agreed upon with remote switch operator\n";
+		return NULL;
+	case CLI_GENERATE:	
+		return NULL;
+	}
+	return handle_pri_service_generic(e, cmd, a, 0);
+}
+
+static char *handle_pri_service_disable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch (cmd) {
+	case CLI_INIT:	
+		e->command = "pri service disable channel";
+		e->usage = 
+			"Usage: pri service disable channel <chan num> [<interface id>]\n"
+			"	Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n"
+			"	to remove a channel from service, with optional interface id\n"
+			"	as agreed upon with remote switch operator\n";
+		return NULL;
+	case CLI_GENERATE:	
+		return NULL;
+	}
+	return handle_pri_service_generic(e, cmd, a, 2);
+}
+#endif
+
 #if defined(HAVE_PRI)
 static void build_status(char *s, size_t len, int status, int active)
 {
@@ -13798,6 +14118,10 @@ static char *handle_pri_version(struct ast_cli_entry *e, int cmd, struct ast_cli
 #if defined(HAVE_PRI)
 static struct ast_cli_entry dahdi_pri_cli[] = {
 	AST_CLI_DEFINE(handle_pri_debug, "Enables PRI debugging on a span"),
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+ 	AST_CLI_DEFINE(handle_pri_service_enable_channel, "Return a channel to service"),
+ 	AST_CLI_DEFINE(handle_pri_service_disable_channel, "Remove a channel from service"),
+#endif
 	AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI Information"),
 	AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI Information"),
 	AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"),
@@ -16581,6 +16905,14 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 #endif
 			} else if (!strcasecmp(v->name, "discardremoteholdretrieval")) {
 				confp->pri.discardremoteholdretrieval = ast_true(v->value);
+#ifdef HAVE_PRI_SERVICE_MESSAGES
+			} else if (!strcasecmp(v->name, "service_message_support")) {
+				/* assuming switchtype for this channel group has been configured already */
+				if ((confp->pri.switchtype == PRI_SWITCH_ATT4ESS || confp->pri.switchtype == PRI_SWITCH_LUCENT5E) && ast_true(v->value))
+					confp->pri.enable_service_message_support = 1;
+				else
+					confp->pri.enable_service_message_support = 0;
+#endif
 #ifdef HAVE_PRI_INBANDDISCONNECT
 			} else if (!strcasecmp(v->name, "inbanddisconnect")) {
 				confp->pri.inbanddisconnect = ast_true(v->value);
diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample
index c3219f3fcc21b5641e023142011d48a4bbdc1042..c0c9cfe5af120f328e65dfe597ae359fbb7930d7 100644
--- a/configs/chan_dahdi.conf.sample
+++ b/configs/chan_dahdi.conf.sample
@@ -74,6 +74,9 @@
 ;
 ;nsf=none
 ;
+;service_message_support=yes
+; Enable service message support for channel. Must be set after switchtype.
+;
 ; PRI Dialplan: The ISDN-level Type Of Number (TON) or numbering plan, used for
 ; the dialed number.  For most installations, leaving this as 'unknown' (the
 ; default) works in the most cases.  In some very unusual circumstances, you
diff --git a/configure b/configure
index 0725b292779e95ee03cefd1b08d94ab667d99bc8..771a1159e7461b8671592b1a2335648312cd6a1d 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 182847 .
+# From configure.ac Revision: 183242 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.61 for asterisk 1.6.
 #
@@ -42037,6 +42037,268 @@ fi
 
 
 
+if test "x${PBX_PRI_SERVICE_MESSAGES}" != "x1" -a "${USE_PRI_SERVICE_MESSAGES}" != "no"; then
+   pbxlibdir=""
+   # if --with-PRI_SERVICE_MESSAGES=DIR has been specified, use it.
+   if test "x${PRI_SERVICE_MESSAGES_DIR}" != "x"; then
+      if test -d ${PRI_SERVICE_MESSAGES_DIR}/lib; then
+      	 pbxlibdir="-L${PRI_SERVICE_MESSAGES_DIR}/lib"
+      else
+      	 pbxlibdir="-L${PRI_SERVICE_MESSAGES_DIR}"
+      fi
+   fi
+   pbxfuncname="pri_maintenance_service"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PRI_SERVICE_MESSAGES_FOUND=yes
+   else
+      as_ac_Lib=`echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for ${pbxfuncname} in -lpri" >&5
+echo $ECHO_N "checking for ${pbxfuncname} in -lpri... $ECHO_C" >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpri ${pbxlibdir}  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  eval "$as_ac_Lib=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	eval "$as_ac_Lib=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval echo '${'$as_ac_Lib'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Lib'}'` = yes; then
+  AST_PRI_SERVICE_MESSAGES_FOUND=yes
+else
+  AST_PRI_SERVICE_MESSAGES_FOUND=no
+fi
+
+   fi
+
+   # now check for the header.
+   if test "${AST_PRI_SERVICE_MESSAGES_FOUND}" = "yes"; then
+      PRI_SERVICE_MESSAGES_LIB="${pbxlibdir} -lpri "
+      # if --with-PRI_SERVICE_MESSAGES=DIR has been specified, use it.
+      if test "x${PRI_SERVICE_MESSAGES_DIR}" != "x"; then
+	 PRI_SERVICE_MESSAGES_INCLUDE="-I${PRI_SERVICE_MESSAGES_DIR}/include"
+      fi
+      PRI_SERVICE_MESSAGES_INCLUDE="${PRI_SERVICE_MESSAGES_INCLUDE} "
+      if test "xlibpri.h" = "x" ; then	# no header, assume found
+         PRI_SERVICE_MESSAGES_HEADER_FOUND="1"
+      else				# check for the header
+         saved_cppflags="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PRI_SERVICE_MESSAGES_INCLUDE}"
+	 if test "${ac_cv_header_libpri_h+set}" = set; then
+  { echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking libpri.h usability" >&5
+echo $ECHO_N "checking libpri.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <libpri.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking libpri.h presence" >&5
+echo $ECHO_N "checking libpri.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <libpri.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: libpri.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: libpri.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: libpri.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: libpri.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: libpri.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: libpri.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: libpri.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: libpri.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: libpri.h: in the future, the compiler will take precedence" >&2;}
+    ( cat <<\_ASBOX
+## ------------------------------- ##
+## Report this to www.asterisk.org ##
+## ------------------------------- ##
+_ASBOX
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for libpri.h" >&5
+echo $ECHO_N "checking for libpri.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_libpri_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_libpri_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_libpri_h" >&5
+echo "${ECHO_T}$ac_cv_header_libpri_h" >&6; }
+
+fi
+if test $ac_cv_header_libpri_h = yes; then
+  PRI_SERVICE_MESSAGES_HEADER_FOUND=1
+else
+  PRI_SERVICE_MESSAGES_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${saved_cppflags}"
+      fi
+      if test "x${PRI_SERVICE_MESSAGES_HEADER_FOUND}" = "x0" ; then
+         PRI_SERVICE_MESSAGES_LIB=""
+         PRI_SERVICE_MESSAGES_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+	    PRI_SERVICE_MESSAGES_LIB=""
+	 fi
+         PBX_PRI_SERVICE_MESSAGES=1
+         # XXX don't know how to evaluate the description (third argument) in AC_DEFINE_UNQUOTED
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_SERVICE_MESSAGES 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PRI_SERVICE_MESSAGES_VERSION
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
 if test "x${PBX_RESAMPLE}" != "x1" -a "${USE_RESAMPLE}" != "no"; then
    pbxlibdir=""
    # if --with-RESAMPLE=DIR has been specified, use it.
diff --git a/configure.ac b/configure.ac
index ac129accc816d6f5c65c5c451cc7533b070078d7..00220d238ed7106cd17d85905d20f92dd50c3d99 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1404,6 +1404,8 @@ AST_EXT_LIB_CHECK([PRI_PROG_W_CAUSE], [pri], [pri_progress_with_cause], [libpri.
 
 AST_EXT_LIB_CHECK([PRI_INBANDDISCONNECT], [pri], [pri_set_inbanddisconnect], [libpri.h])
 
+AST_EXT_LIB_CHECK([PRI_SERVICE_MESSAGES], [pri], [pri_maintenance_service], [libpri.h])
+
 AST_EXT_LIB_CHECK([RESAMPLE], [resample], [resample_open], [libresample.h], [-lm])
 
 AST_C_COMPILE_CHECK([SPANDSP], [
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index dc95c83929457e6fb6fac3b898f130274c47856e..dba007d47a9343eeefc69079eb65757a7c03584d 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -671,6 +671,12 @@
 /* Define to indicate the ${PRI_PROG_W_CAUSE_DESCRIP} library version */
 #undef HAVE_PRI_PROG_W_CAUSE_VERSION
 
+/* Define this to indicate the ${PRI_SERVICE_MESSAGES_DESCRIP} library */
+#undef HAVE_PRI_SERVICE_MESSAGES
+
+/* Define to indicate the ${PRI_SERVICE_MESSAGES_DESCRIP} library version */
+#undef HAVE_PRI_SERVICE_MESSAGES_VERSION
+
 /* Define to indicate the ${PRI_DESCRIP} library version */
 #undef HAVE_PRI_VERSION