diff -dur asterisk-1.0.7_original/channels/chan_zap.c asterisk-1.0.7_echomaster_v11/channels/chan_zap.c
--- asterisk-1.0.7_original/channels/chan_zap.c	2005-02-25 18:11:41.000000000 +0000
+++ asterisk-1.0.7_echomaster_v11/channels/chan_zap.c	2005-07-26 15:17:50.789354032 +0000
@@ -153,6 +153,134 @@
 
 #define DCHAN_AVAILABLE	(DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP)
 
+////////////////////////////////////////////////////////////////////////
+
+#define TDMV_SAMPLE_STATE_DECODE(state)			\
+	((state == ECHO_PRESENT) ? "On" :		\
+	 (state == ECHO_ABSENT) ?  "Off" : 		\
+         (state == INDETERMINATE) ? "?" : "Invalid")
+
+#define TMP_BUFF_LEN	512
+
+static unsigned short u2s[] = {
+ /* negative */
+ 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84,
+ 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
+ 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84,
+ 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
+ 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804,
+ 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
+ 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444,
+ 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
+ 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64,
+ 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
+ 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74,
+ 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
+ 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC,
+ 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
+ 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0,
+ 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
+ /* positive */
+ 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C,
+ 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
+ 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C,
+ 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
+ 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC,
+ 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
+ 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC,
+ 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
+ 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C,
+ 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
+ 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C,
+ 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
+ 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+ 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
+ 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+ 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+#if 1
+//ZAPTEL table
+static unsigned short a2s[] = {
+
+0xEB00, 0xEC00, 0xE900, 0xEA00, 0xEF00, 0xF000, 0xED00, 0xEE00,
+0xE300, 0xE400, 0xE100, 0xE200, 0xE700, 0xE800, 0xE500, 0xE600, 
+0xF580, 0xF600, 0xF480, 0xF500, 0xF780, 0xF800, 0xF680, 0xF700, 
+0xF180, 0xF200, 0xF080, 0xF100, 0xF380, 0xF400, 0xF280, 0xF300, 
+0xAC00, 0xB000, 0xA400, 0xA800, 0xBC00, 0xC000, 0xB400, 0xB800, 
+0x8C00, 0x9000, 0x8400, 0x8800, 0x9C00, 0xA000, 0x9400, 0x9800, 
+0xD600, 0xD800, 0xD200, 0xD400, 0xDE00, 0xE000, 0xDA00, 0xDC00, 
+0xC600, 0xC800, 0xC200, 0xC400, 0xCE00, 0xD000, 0xCA00, 0xCC00, 
+0xFEB0, 0xFEC0, 0xFE90, 0xFEA0, 0xFEF0, 0xFF00, 0xFED0, 0xFEE0, 
+0xFE30, 0xFE40, 0xFE10, 0xFE20, 0xFE70, 0xFE80, 0xFE50, 0xFE60, 
+0xFFB0, 0xFFC0, 0xFF90, 0xFFA0, 0xFFF0, 0x0000, 0xFFD0, 0xFFE0, 
+0xFF30, 0xFF40, 0xFF10, 0xFF20, 0xFF70, 0xFF80, 0xFF50, 0xFF60, 
+0xFAC0, 0xFB00, 0xFA40, 0xFA80, 0xFBC0, 0xFC00, 0xFB40, 0xFB80, 
+0xF8C0, 0xF900, 0xF840, 0xF880, 0xF9C0, 0xFA00, 0xF940, 0xF980, 
+0xFD60, 0xFD80, 0xFD20, 0xFD40, 0xFDE0, 0xFE00, 0xFDA0, 0xFDC0, 
+0xFC60, 0xFC80, 0xFC20, 0xFC40, 0xFCE0, 0xFD00, 0xFCA0, 0xFCC0, 
+
+0x1500, 0x1400, 0x1700, 0x1600, 0x1100, 0x1000, 0x1300, 0x1200, 
+0x1D00, 0x1C00, 0x1F00, 0x1E00, 0x1900, 0x1800, 0x1B00, 0x1A00, 
+0x0A80, 0x0A00, 0x0B80, 0x0B00, 0x0880, 0x0800, 0x0980, 0x0900, 
+0x0E80, 0x0E00, 0x0F80, 0x0F00, 0x0C80, 0x0C00, 0x0D80, 0x0D00, 
+0x5400, 0x5000, 0x5C00, 0x5800, 0x4400, 0x4000, 0x4C00, 0x4800, 
+0x7400, 0x7000, 0x7C00, 0x7800, 0x6400, 0x6000, 0x6C00, 0x6800, 
+0x2A00, 0x2800, 0x2E00, 0x2C00, 0x2200, 0x2000, 0x2600, 0x2400, 
+0x3A00, 0x3800, 0x3E00, 0x3C00, 0x3200, 0x3000, 0x3600, 0x3400, 
+0x0150, 0x0140, 0x0170, 0x0160, 0x0110, 0x0100, 0x0130, 0x0120, 
+0x01D0, 0x01C0, 0x01F0, 0x01E0, 0x0190, 0x0180, 0x01B0, 0x01A0, 
+0x0050, 0x0040, 0x0070, 0x0060, 0x0010, 0x0000, 0x0030, 0x0020, 
+0x00D0, 0x00C0, 0x00F0, 0x00E0, 0x0090, 0x0080, 0x00B0, 0x00A0, 
+0x0540, 0x0500, 0x05C0, 0x0580, 0x0440, 0x0400, 0x04C0, 0x0480, 
+0x0740, 0x0700, 0x07C0, 0x0780, 0x0640, 0x0600, 0x06C0, 0x0680, 
+0x02A0, 0x0280, 0x02E0, 0x02C0, 0x0220, 0x0200, 0x0260, 0x0240,
+0x03A0, 0x0380, 0x03E0, 0x03C0, 0x0320, 0x0300, 0x0360, 0x0340
+};
+#endif
+
+#if 0
+//YATE table
+static unsigned short a2s[] = {
+ /* negative */
+ 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80,
+ 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
+ 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0,
+ 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
+ 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600,
+ 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
+ 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00,
+ 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
+ 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8,
+ 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
+ 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8,
+ 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
+ 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60,
+ 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
+ 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0,
+ 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
+ /* positive */
+ 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280,
+ 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
+ 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940,
+ 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
+ 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00,
+ 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
+ 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500,
+ 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
+ 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128,
+ 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
+ 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028,
+ 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
+ 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0,
+ 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
+ 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250,
+ 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
+};
+#endif
+
+////////////////////////////////////////////////////////////////////////
+
 #define zt_close(fd) if(fd > 0) close(fd);
 
 static char context[AST_MAX_EXTENSION] = "default";
@@ -439,6 +567,13 @@
 	struct zt_subchannel subs[3];	/* Sub-channels */
 	struct zt_confinfo saveconf;	/* Saved conference info */
 
+	int echo_detection_state;
+	pthread_t polling_thread_id;
+	int is_call_active;
+	int ed_enabled;
+	unsigned int num_of_echo_absent_calls;
+	unsigned int num_of_echo_present_calls;
+
 	struct zt_pvt *slaves[MAX_SLAVES];	/* Slave to us (follows our conferencing) */
 	struct zt_pvt *master;	/* Master to us (we follow their conferencing) */
 	int inconference;		/* If our real should be in the conference */
@@ -575,6 +710,32 @@
 #define GET_CHANNEL(p) ((p)->channel)
 #endif
 
+static int sangoma_get_echo_state(struct zt_pvt* pvt, echo_detect_struct_t * ioctl_result);
+static int sangoma_write_call_started_str(struct zt_pvt *pvt);
+static int sangoma_write_call_ended_str(struct zt_pvt *pvt);
+static char* sangoma_get_date_and_time(void);
+static void sangoma_ed_control(int fd, struct zt_pvt *tmp, int control_code);
+static void sangoma_linearize_spike_buffer(int fd, unsigned char *spike_buff, int *linearized_buff, int law);
+static int sangoma_write_spike_buffer_to_file(struct zt_pvt *pvt, int *linearized_buff, int fd);
+static int sangoma_write_buffer_to_file(char * full_file_path, char* string);
+static void sangoma_summary_of_linear_spike_buffer(int fd, int *linearized_buff, struct zt_pvt *tmp);
+static void sangoma_analyze_db_loss(int fd, struct zt_pvt *tmp);
+
+#define INITIAL_LINEAR_TX_SPIKE 16384
+#define NO	0
+#define YES 	1
+#define DB_LOSS_THRESHOLD	-45.00//-50.00//-60.00
+#define DB_SILENCE		80.00
+
+typedef struct{
+	float db_loss;
+	short linear_power_value;
+}db_sample_t;
+
+db_sample_t db_loss[SANGOMA_SPIKE_SAMPLE_LEN];
+
+void proc_sample(unsigned short, int);
+
 struct zt_pvt *round_robin[32];
 
 #ifdef ZAPATA_PRI
@@ -1217,6 +1378,12 @@
 		}
 		x = p->echocancel;
 		res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL, &x);
+
+		if(p->ed_enabled == 1 && p->is_call_active == 0){
+			p->is_call_active = 1;
+			sangoma_write_call_started_str(p);
+		}
+
 		if (res) 
 			ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d\n", p->channel);
 		else {
@@ -1247,9 +1414,42 @@
 {
 	int x;
 	int res;
+	
+	echo_detect_struct_t ioctl_result;
+	char log_str_buff[TMP_BUFF_LEN];
+	char log_file_name_buff[TMP_BUFF_LEN];
+
+
 	if (p->echocancel) {
 		x = 0;
 		res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL, &x);
+
+		if(p->is_call_active == 1){	
+			/* This function is called twice, without obvious reason,
+			   but end of call should be recorded only once.
+			*/
+			p->is_call_active = 0;
+
+			if(!sangoma_get_echo_state(p, &ioctl_result)){
+
+				snprintf(log_file_name_buff, TMP_BUFF_LEN, 
+					"/etc/wanpipe/span_%d_chan_%d.log", p->span, p->channel);
+
+				//if(ioctl_result.echo_state != INDETERMINATE){
+					snprintf(log_str_buff, TMP_BUFF_LEN, 
+						" %-3s ", TDMV_SAMPLE_STATE_DECODE(ioctl_result.echo_state));
+					sangoma_write_buffer_to_file(log_file_name_buff, log_str_buff);
+				//}
+
+				if(ioctl_result.echo_state == ECHO_ABSENT){
+					p->num_of_echo_absent_calls++;
+				}if(ioctl_result.echo_state == ECHO_PRESENT){
+					p->num_of_echo_present_calls++;
+				}
+			}//if()
+			sangoma_write_call_ended_str(p);
+		}//if(p->is_call_active == 1)
+
 		if (res) 
 			ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d\n", p->channel);
 		else
@@ -1299,11 +1499,167 @@
 			g.txgain[j] = j;
 		}
 	}
-		
-	  /* set 'em */
+	
+	 /* set 'em */
 	return(ioctl(fd,ZT_SETGAINS,&g));
 }
 
+int usecsleep(int sec, int usecs)
+{
+	struct timeval tv;
+
+	tv.tv_sec = sec;
+	tv.tv_usec = usecs;
+
+	return select(0, NULL, NULL, NULL, &tv);
+}
+
+static char* sangoma_get_date_and_time()
+{
+	struct timeval tv;
+	struct timezone tz;
+	time_t now;
+	static char tmp_str[TMP_BUFF_LEN];
+	int ind;
+
+	gettimeofday(&tv, &tz);
+	now = tv.tv_sec;
+
+	snprintf(tmp_str, TMP_BUFF_LEN, ctime(&now));
+	/* replace 'new line' with 'null' */
+	for(ind = 0; ind < TMP_BUFF_LEN; ind++){
+		if(tmp_str[ind] == '\n'){
+			tmp_str[ind] = '\0';
+		}
+	}
+
+	return tmp_str;
+}
+
+#define MAX_LOG_FILE_SIZE	500000	// 500 Kb is the maximum
+
+int sangoma_write_buffer_to_file(char * full_file_path, char* string)
+{
+	FILE * file;
+	struct stat statbuf;
+
+	file = fopen(full_file_path, "a");
+	if(file == NULL){
+    		ast_log(LOG_WARNING, "Failed to open %s file for writing!\n", full_file_path);
+		return 1;
+  	}
+
+	//file exist, let's make sure it's not too big
+	if(lstat(full_file_path, &statbuf)){
+    		ast_log(LOG_WARNING, "Failed to get file size ! File: %s.\n", full_file_path);
+		return 2;
+	}
+
+	if(statbuf.st_size > MAX_LOG_FILE_SIZE){
+    		ast_log(LOG_WARNING, "File %s reached maximum size!\n", full_file_path);
+		return 3;
+	}
+	
+  	fputs(string, file);
+	fclose(file);
+	return 0;
+}
+
+static int sangoma_write_spike_buffer_to_file(struct zt_pvt *pvt, int *linearized_buff, int fd)
+{
+	FILE *file;
+	int ind;
+	char strbuff[50];
+	char log_file_name_buff[TMP_BUFF_LEN];
+		
+	snprintf(log_file_name_buff, TMP_BUFF_LEN, 
+		"/etc/wanpipe/span_%d_chan_%d.spike", pvt->span, pvt->channel);
+
+	/* trancate and open the file for writing */
+	file = fopen(log_file_name_buff, "w");
+	if(file == NULL){
+    		ast_log(LOG_WARNING, "Failed to open %s file for writing!\n", log_file_name_buff);
+		return 1;
+  	}
+
+	for(ind = 0; ind < SANGOMA_SPIKE_SAMPLE_LEN; ind++){
+		snprintf(strbuff, 50, "%d\n", linearized_buff[ind]);
+  		fputs(strbuff, file);
+	}/* for() */
+	fclose(file);
+
+	ast_cli(fd,  "Echo spike was written to '%s' file.\n", log_file_name_buff);
+
+	return 0;
+}
+
+static int sangoma_do_echo_detect_ioctl(int zfd, int echo_detect_ioctl, int * ioctl_result)
+{
+	if (ioctl(zfd, echo_detect_ioctl, ioctl_result)){
+		perror("sangoma_do_echo_detect_ioctl():\n");
+		return 1;
+	}else{
+		return 0;
+	}
+}
+
+static int sangoma_get_echo_state(struct zt_pvt* pvt, echo_detect_struct_t* ioctl_result)
+{
+	if(pvt == NULL){
+		ast_log(LOG_WARNING, "%s():pvt is NULL!!!\n", __FUNCTION__);
+		return 1;
+	}
+
+//	ast_log(LOG_WARNING, "span: %d, channel: %d\n", pvt->span, pvt->channel);
+//	ast_log(LOG_WARNING, "zdf: %d\n", pvt->subs[SUB_REAL].zfd);
+
+	if(!sangoma_do_echo_detect_ioctl(pvt->subs[SUB_REAL].zfd, SANGOMA_GET_ED_STATE, (int*)ioctl_result)){
+		return 0;
+	}else{
+		return 3;
+	}
+}
+
+static int sangoma_write_call_started_str(struct zt_pvt *pvt)
+{
+	char log_str_buff[TMP_BUFF_LEN];
+	char log_file_name_buff[TMP_BUFF_LEN];
+
+	snprintf(log_file_name_buff, TMP_BUFF_LEN, 
+		"/etc/wanpipe/span_%d_chan_%d.log", pvt->span, pvt->channel);
+
+	snprintf(log_str_buff, TMP_BUFF_LEN, "START: [%-24s]",
+		sangoma_get_date_and_time());
+	sangoma_write_buffer_to_file(log_file_name_buff, log_str_buff);
+
+	if(pvt->owner){
+		snprintf(log_str_buff, TMP_BUFF_LEN, "[exten:%s, callerid:%s]",
+			(pvt->owner->exten == NULL   ? "----" : pvt->owner->exten),
+			(pvt->owner->callerid == NULL? "----" : pvt->owner->callerid));
+	}else{
+		snprintf(log_str_buff, TMP_BUFF_LEN, "[context:%s, exten:%s, callerid:%s]",
+			"----", "----", "----");
+	}
+
+	return sangoma_write_buffer_to_file(log_file_name_buff, log_str_buff);
+}
+
+static int sangoma_write_call_ended_str(struct zt_pvt *pvt)
+{
+	char log_str_buff[TMP_BUFF_LEN];
+	char log_file_name_buff[TMP_BUFF_LEN];
+
+	snprintf(log_file_name_buff, TMP_BUFF_LEN, 
+		"/etc/wanpipe/span_%d_chan_%d.log", pvt->span, pvt->channel);
+
+	snprintf(log_str_buff, TMP_BUFF_LEN, "END: [%s][eabs:%u,epres:%u]\n",
+		sangoma_get_date_and_time(),
+		pvt->num_of_echo_absent_calls,
+		pvt->num_of_echo_present_calls);
+
+	return sangoma_write_buffer_to_file(log_file_name_buff, log_str_buff);
+}
+
 static inline int zt_set_hook(int fd, int hs)
 {
 	int x, res;
@@ -8622,6 +8978,382 @@
 	return RESULT_FAILURE;
 }
 
+static int sangoma_show_channels_func(int fd, int argc, char **argv)
+{
+	struct zt_pvt *tmp = NULL;
+	ast_mutex_t *lock;
+	struct zt_pvt *start;
+	echo_detect_struct_t ioctl_result;
+	int active_chans_counter;
+	
+	lock = &iflock;
+	start = iflist;
+
+	ast_mutex_lock(lock);
+	tmp = start;
+	
+	ast_cli(fd, "\nSpan\tChannel\t\tEcho\tContext\t\tExtension\tCallerID\n");
+
+	active_chans_counter = 0;
+	while (tmp){
+		if(tmp->channel < 0 || 
+		   sangoma_get_echo_state(tmp, &ioctl_result) ||
+		   ioctl_result.echo_detection_state != ECHO_DETECT_ON){/* show only active channels*/
+			tmp = tmp->next;
+			continue;
+		}
+	
+		active_chans_counter++;
+
+		ast_cli(fd, "%02d\t%02d\t\t%-5s\t%-10s\t%-10s\t%-10s\n",
+			tmp->span,
+			tmp->channel,
+			TDMV_SAMPLE_STATE_DECODE(ioctl_result.echo_state),
+
+			(tmp->owner->context == NULL ? "----" : tmp->owner->context),
+			(tmp->owner->exten == NULL   ? "----" : tmp->owner->exten),
+			(tmp->owner->callerid == NULL? "----" : tmp->owner->callerid)
+			);
+
+		tmp = tmp->next;
+	}/* while() */
+	ast_cli(fd, "%d active channel(s)\n", active_chans_counter);
+	
+	ast_mutex_unlock(lock);
+	return RESULT_SUCCESS;
+}
+
+static int sangoma_dbg_channel(int fd, int argc, char **argv)
+{
+	int channel;
+	struct zt_pvt *tmp = NULL;
+	ast_mutex_t *lock;
+	struct zt_pvt *start;
+	
+	lock = &iflock;
+	start = iflist;
+
+	if (argc != 4){
+		return RESULT_SHOWUSAGE;
+	}
+
+	channel = atoi(argv[3]);
+
+	ast_mutex_lock(lock);
+	tmp = start;
+
+	/* The ED algorithm can be enabled/disabled on all channels or
+	   for an individual channel.
+	*/
+	if(!strcmp(argv[3], "all")){
+		while (tmp) {
+			if(tmp->channel > 0){
+				if(!strcmp(argv[1], "enable")){
+					sangoma_ed_control(fd, tmp, SANGOMA_ENABLE_ED);
+				}else if(!strcmp(argv[1], "disable")){
+					sangoma_ed_control(fd, tmp, SANGOMA_DISABLE_ED);
+				}else{
+					ast_cli(fd, "%s(): Invalid command switch: %s!\n",
+						argv[2], __FUNCTION__);
+				}
+			}
+			tmp = tmp->next;
+		}/* while() */
+		ast_mutex_unlock(lock);
+		return RESULT_SUCCESS;
+	}else{
+		while (tmp) {
+			if (tmp->channel == channel) {
+
+				if(!strcmp(argv[1], "enable")){
+					sangoma_ed_control(fd, tmp, SANGOMA_ENABLE_ED);
+				}else if(!strcmp(argv[1], "disable")){
+					sangoma_ed_control(fd, tmp, SANGOMA_DISABLE_ED);
+				}else{
+					ast_cli(fd, "%s(): Invalid command switch: %s!\n",
+						argv[2], __FUNCTION__);
+				}
+
+				ast_mutex_unlock(lock);
+				return RESULT_SUCCESS;
+			}
+			tmp = tmp->next;
+		}/* while() */
+	}/* if() */
+
+	ast_cli(fd, "Unable to find given channel %d\n", channel);
+	ast_mutex_unlock(lock);
+	return RESULT_FAILURE;
+}
+
+static void sangoma_ed_control(int fd, struct zt_pvt *tmp, int control_code)
+{
+	echo_detect_struct_t ioctl_result;
+
+	switch(control_code)
+	{
+	case SANGOMA_ENABLE_ED:
+		ast_cli(fd, 
+			"Enabling Sangoma ED on  Span: %d, Channel: %d, File Descriptor: %d.\n", 
+			tmp->span, tmp->channel, tmp->subs[SUB_REAL].zfd);
+		tmp->ed_enabled = 1;
+
+		sangoma_do_echo_detect_ioctl(tmp->subs[SUB_REAL].zfd, SANGOMA_ENABLE_ED,
+						(int*)&ioctl_result);
+		break;
+
+	case SANGOMA_DISABLE_ED:
+		ast_cli(fd, 
+			"Disabling Sangoma ED on  Span: %d, Channel: %d, File Descriptor: %d.\n", 
+			tmp->span, tmp->channel, tmp->subs[SUB_REAL].zfd);
+		tmp->ed_enabled = 0;
+
+		sangoma_do_echo_detect_ioctl(tmp->subs[SUB_REAL].zfd, SANGOMA_DISABLE_ED,
+						(int*)&ioctl_result);
+		break;
+	default:
+		ast_cli(fd, "%s(): invalid IOCTL code.\n", __FUNCTION__); 
+	}
+}
+
+static int sangoma_send_echo_spike(int fd, int argc, char **argv)
+{
+	int channel;
+	struct zt_pvt *tmp = NULL;
+	ast_mutex_t *lock;
+	struct zt_pvt *start;
+	unsigned char spike_buff[sizeof(echo_spike_struct_t)];
+	echo_spike_struct_t *echo_spike_struct_ptr = (echo_spike_struct_t *)spike_buff;
+
+	lock = &iflock;
+	start = iflist;
+
+	if (argc != 4){
+		return RESULT_SHOWUSAGE;
+	}
+
+	channel = atoi(argv[3]);
+
+	ast_mutex_lock(lock);
+	tmp = start;
+
+	while (tmp) {
+		if (tmp->channel == channel) {
+			if(sangoma_do_echo_detect_ioctl(	tmp->subs[SUB_REAL].zfd,
+							SANGOMA_SEND_SPIKE,
+							(int*)spike_buff) != 0){
+				ast_cli(fd, 
+					"Command failed on channel %d! Please check kernel messages log.\n", 
+					channel);
+			}else if(echo_spike_struct_ptr->return_code == SANGOMA_NO_ACTIVE_CALL_OR_EC_OFF){
+				ast_cli(fd, 
+					"Command failed on channel %d! \
+ Possible reasons: 1. the 'echocancel' is set to 'no' 2. call is not up.\n", channel);
+			}else{
+				;/* no error. do nothing. */
+			}
+			ast_mutex_unlock(lock);
+			return RESULT_SUCCESS;
+		}
+		tmp = tmp->next;
+	}/* while() */
+
+	ast_cli(fd, "Unable to find given channel %d\n", channel);
+	ast_mutex_unlock(lock);
+	return RESULT_FAILURE;
+}
+
+static int sangoma_get_echo_spike(int fd, int argc, char **argv)
+{
+	int channel;
+	struct zt_pvt *tmp = NULL;
+	ast_mutex_t *lock;
+	struct zt_pvt *start;
+	static unsigned char spike_buff[sizeof(echo_spike_struct_t) + SANGOMA_SPIKE_SAMPLE_LEN];
+	echo_spike_struct_t *echo_spike_struct_ptr = (echo_spike_struct_t *)spike_buff;
+	unsigned char *spike_sample_buff_ptr = &spike_buff[sizeof(echo_spike_struct_t)];
+	int linearized_buff[SANGOMA_SPIKE_SAMPLE_LEN];
+	
+	lock = &iflock;
+	start = iflist;
+
+	if (argc != 4){
+		return RESULT_SHOWUSAGE;
+	}
+
+	channel = atoi(argv[3]);
+
+	ast_mutex_lock(lock);
+	tmp = start;
+
+	while (tmp) {
+		if (tmp->channel == channel) {
+			if(sangoma_do_echo_detect_ioctl(tmp->subs[SUB_REAL].zfd,
+							SANGOMA_GET_ECHO_SPIKE_SAMPLE,
+							(int*)spike_buff) == 0){
+				if(echo_spike_struct_ptr->return_code == SANGOMA_OK){
+					/*
+					int ind;
+					ast_cli(fd,  "SANGOMA_SPIKE_SAMPLE_LEN:%d\n", SANGOMA_SPIKE_SAMPLE_LEN);
+					ast_cli(fd,  "NOT liniarized buffer:\n");
+					for(ind = 0; ind < SANGOMA_SPIKE_SAMPLE_LEN; ind++){
+						ast_cli(fd,  "0x%02X\n", spike_sample_buff_ptr[ind]);
+					}
+					*/
+					sangoma_linearize_spike_buffer(fd, spike_sample_buff_ptr, linearized_buff,
+						tmp->law);
+
+					sangoma_write_spike_buffer_to_file(tmp, linearized_buff, fd);
+					
+					sangoma_summary_of_linear_spike_buffer(fd, linearized_buff, tmp);
+				}else{
+					ast_cli(fd, 
+						"Command failed on channel %d! \
+ Possible reasons: 1. the 'echocancel' is set to 'no' 2. call is not up.\n", channel);
+				}
+			}
+			ast_mutex_unlock(lock);
+			return RESULT_SUCCESS;
+		}
+		tmp = tmp->next;
+	}/* while() */
+
+	ast_cli(fd, "Unable to find given channel %d\n", channel);
+	ast_mutex_unlock(lock);
+	return RESULT_FAILURE;
+}
+
+static void sangoma_linearize_spike_buffer(int fd, unsigned char *spike_buff, int *linearized_buff, int law)
+{
+	int ind;
+	unsigned short *linear_table;
+
+	if(law == ZT_LAW_MULAW){
+		ast_cli(fd,  "Using MULAW table...\n");
+		linear_table = u2s;
+	}else{
+		ast_cli(fd,  "Using ALAW table...\n");
+		linear_table = a2s;
+	}
+	
+	memset(linearized_buff, 0x00, SANGOMA_SPIKE_SAMPLE_LEN);
+
+	//ast_cli(fd,  "linearized buffer (v2 len: %d)\n", SANGOMA_SPIKE_SAMPLE_LEN);
+	for(ind = 0; ind < SANGOMA_SPIKE_SAMPLE_LEN; ind++){
+		
+		if(linear_table[spike_buff[ind]] == 0){
+			linearized_buff[ind] = 0;
+		}else if(spike_buff[ind] < 128){
+			/* make it negative */
+			linearized_buff[ind] = -65535 + linear_table[spike_buff[ind]];
+		}else{
+			linearized_buff[ind] = linear_table[spike_buff[ind]];
+		}
+		//ast_cli(fd, "%d\n", linearized_buff[ind]);
+	}/* for() */
+}
+
+static void sangoma_summary_of_linear_spike_buffer(int fd, int *linearized_buff, struct zt_pvt *tmp)
+{
+	int i;
+
+	for(i = 0; i < SANGOMA_SPIKE_SAMPLE_LEN; i ++) {
+		proc_sample(linearized_buff[i], i);
+	}
+	sangoma_analyze_db_loss(fd, tmp);
+}
+
+void proc_sample(unsigned short linear, int sample_no)
+{
+	unsigned short val;
+	float d;
+	float db;
+	int neg_voltage = NO;
+
+	db_loss[sample_no].linear_power_value = linear;
+
+	if(linear & 0xF000) {
+		neg_voltage = YES;
+		val = linear ^ 0xFFFF;
+	} else {
+		val = linear;
+	}
+		
+	val = val >> 3;
+	val &= 0x0FFF;
+	d = (float)val / (float)(INITIAL_LINEAR_TX_SPIKE >> 3);
+
+	if(d != 0.00) {
+		db = 20 * log10(d);
+	} else {
+		db = -DB_SILENCE;
+	}
+
+	db_loss[sample_no].db_loss = db;
+
+//	printf("sample[%04d]%2.5f\n", sample_no, db);
+	return;
+}
+
+static void sangoma_analyze_db_loss(int fd, struct zt_pvt *tmp)
+{
+	float min_db_loss = -DB_SILENCE;
+	int min_db_loss_sample_no=0;
+	float first_db_loss_th;
+	int sample_no_first_db_loss_th = 0xFFFF;
+	float last_db_loss_th;
+	int sample_no_last_db_loss_th = 0xFFFF;
+	int i;
+
+	for(i = 0; i < SANGOMA_SPIKE_SAMPLE_LEN; i ++) {
+		if(db_loss[i].db_loss > min_db_loss) {
+			min_db_loss = db_loss[i].db_loss;
+			min_db_loss_sample_no = i;
+		}
+		if(sample_no_first_db_loss_th == 0xFFFF) {
+			if(db_loss[i].db_loss > DB_LOSS_THRESHOLD) {
+				sample_no_first_db_loss_th = i;
+				first_db_loss_th = db_loss[i].db_loss;
+			}
+		}
+		if(db_loss[i].db_loss > DB_LOSS_THRESHOLD) {
+			last_db_loss_th = db_loss[i].db_loss;
+			sample_no_last_db_loss_th = i;
+		}
+	}
+
+	if(min_db_loss == -DB_SILENCE) {
+		ast_cli(fd,  "The line is silent. No conclusion can be made.\n");
+	} else {
+		ast_cli(fd,  "\nEcho Spike reached it's maximum at sample %d, linear power is %d\n", 
+			min_db_loss_sample_no + 1,
+			db_loss[min_db_loss_sample_no].linear_power_value);
+
+		ast_cli(fd,  "First sample containing Echo Spike is %d, linear power is %d\n", 
+			sample_no_first_db_loss_th + 1,
+			db_loss[sample_no_first_db_loss_th].linear_power_value);
+
+		ast_cli(fd,  "Last sample containing Echo Spike is %d, linear power is %d\n", 
+			sample_no_last_db_loss_th + 1,
+			db_loss[sample_no_last_db_loss_th].linear_power_value);
+
+		//suggest something depending on number of Echo Cancellor taps
+		ast_cli(fd,  "\nYour Echo Cancellor set to use %d taps.\n", tmp->echocancel);
+		if(tmp->echocancel < sample_no_first_db_loss_th + 1){
+			ast_cli(fd,  "It can not cancel the echo because delay is longer than %d taps.\n",
+				tmp->echocancel);	
+	
+		}else if(tmp->echocancel > sample_no_first_db_loss_th + 1 &&
+		    tmp->echocancel < sample_no_last_db_loss_th + 1){
+			ast_cli(fd,  "Echo starts WITHIN %d taps, but ends AFTER %d taps, it means\n\
+echo can not be cancelled completely.\n", tmp->echocancel, tmp->echocancel);	
+		}else{
+			ast_cli(fd,  "Echo starts and ends WITHIN %d taps, it should be cancelled properly.\n",
+				tmp->echocancel);	
+		}
+	}	
+}
+
 static char zap_show_cadences_help[] =
 "Usage: zap show cadences\n"
 "       Shows all cadences currently defined\n";
@@ -8674,6 +9406,58 @@
 static struct ast_cli_entry cli_show_channel = { 
 	{"zap", "show", "channel", NULL}, zap_show_channel, "Show information on a channel", show_channel_usage, NULL };
 
+/////////////////////////////////////////////////////////////////////////////////////////
+static char sangoma_show_channels_usage[] = "Usage: sangoma show channels\n";
+
+static struct ast_cli_entry sangoma_show_channels = { 
+	{"sangoma", "show", "channels", NULL},
+	sangoma_show_channels_func, 
+	"Show Sangoma channels. Only shows channels with ED enabled and a call up.",
+	sangoma_show_channels_usage,
+	NULL };
+
+//--------------------------------------------------------------------------------------
+static char sangoma_show_chan_debug_usage[] = "Usage: sangoma enable edchannel <chan num> OR <all>\n";
+
+static struct ast_cli_entry sangoma_debug_chan = { 
+	{"sangoma", "enable", "edchannel", NULL},
+	sangoma_dbg_channel, 
+	"Enable ED algorithm on Sangoma channel(s)",
+	sangoma_show_chan_debug_usage,
+	NULL };
+
+//--------------------------------------------------------------------------------------
+static char sangoma_show_chan_nodebug_usage[] = "Usage: sangoma disable edchannel <chan num> OR <all>\n";
+
+static struct ast_cli_entry sangoma_nodebug_chan = { 
+	{"sangoma", "disable", "edchannel", NULL},
+	sangoma_dbg_channel, 
+	"Disable ED algorithm on Sangoma channel(s)",
+	sangoma_show_chan_nodebug_usage,
+	NULL };
+
+//--------------------------------------------------------------------------------------
+static char sangoma_get_echo_spike_sample_usage[] = "Usage: sangoma get espike <chan num>\n";
+
+static struct ast_cli_entry sangoma_get_echo_spike_chan = { 
+	{"sangoma", "get", "espike", NULL},
+	sangoma_get_echo_spike, 
+	"Get Echo spike sample buffer for a channel",
+	sangoma_get_echo_spike_sample_usage,
+	NULL };
+
+//--------------------------------------------------------------------------------------
+static char sangoma_send_echo_spike_usage[] = "Usage: sangoma send espike <chan num>\n";
+
+static struct ast_cli_entry sangoma_send_echo_spike_chan = { 
+	{"sangoma", "send", "espike", NULL},
+	sangoma_send_echo_spike, 
+	"Send Echo spike on a channel",
+	sangoma_send_echo_spike_usage,
+	NULL };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
 static struct ast_cli_entry cli_destroy_channel = { 
 	{"zap", "destroy", "channel", NULL}, zap_destroy_channel, "Destroy a channel", destroy_channel_usage, NULL };
 
@@ -8891,6 +9675,12 @@
 #endif
 	ast_cli_unregister(&cli_show_channels);
 	ast_cli_unregister(&cli_show_channel);
+	ast_cli_unregister(&sangoma_show_channels);
+	ast_cli_unregister(&sangoma_debug_chan);
+	ast_cli_unregister(&sangoma_nodebug_chan);
+	ast_cli_unregister(&sangoma_get_echo_spike_chan);
+	ast_cli_unregister(&sangoma_send_echo_spike_chan);
+
 	ast_cli_unregister(&cli_destroy_channel);
 	ast_cli_unregister(&zap_show_cadences_cli);
 	ast_manager_unregister( "ZapDialOffhook" );
@@ -9694,9 +10484,13 @@
 #endif	
 	ast_cli_register(&cli_show_channels);
 	ast_cli_register(&cli_show_channel);
+	ast_cli_register(&sangoma_show_channels);
+	ast_cli_register(&sangoma_debug_chan);
+	ast_cli_register(&sangoma_nodebug_chan);
+	ast_cli_register(&sangoma_get_echo_spike_chan);
+	ast_cli_register(&sangoma_send_echo_spike_chan);
 	ast_cli_register(&cli_destroy_channel);
 	ast_cli_register(&zap_show_cadences_cli);
-
 	ast_register_application(app_callingpres, change_callingpres, synopsis_callingpres, descrip_callingpres);
 	memset(round_robin, 0, sizeof(round_robin));
 	ast_manager_register( "ZapTransfer", 0, action_transfer, "Transfer Zap Channel" );
