Skip to content
Snippets Groups Projects
chan_misdn.c 104 KiB
Newer Older
Russell Bryant's avatar
Russell Bryant committed
 * Asterisk -- An open source telephony toolkit.
 * 
 * Copyright (C) 2004 - 2006, Christian Richter
 *
 * Christian Richter <crich@beronet.com>
 *
Russell Bryant's avatar
Russell Bryant committed
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
Russell Bryant's avatar
Russell Bryant committed
 * the GNU General Public License Version 2. See the LICENSE file
 * at the top of the source tree.
 *
 */

/*!
 * \file
 *
 * \brief the chan_misdn channel driver for Asterisk
 * \author Christian Richter <crich@beronet.com>
 *
 * \ingroup channel_drivers
 */

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/file.h>

#include <asterisk/channel.h>
#include <asterisk/config.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/io.h>
#include <asterisk/frame.h>
#include <asterisk/translate.h>
#include <asterisk/cli.h>
#include <asterisk/musiconhold.h>
#include <asterisk/dsp.h>
#include <asterisk/translate.h>
#include <asterisk/config.h>
#include <asterisk/file.h>
#include <asterisk/callerid.h>
#include <asterisk/indications.h>
#include <asterisk/app.h>
#include <asterisk/features.h>
#include <asterisk/stringfields.h>
#include <chan_misdn_config.h>
#include <isdn_lib.h>
Kevin P. Fleming's avatar
Kevin P. Fleming committed
ast_mutex_t release_lock_mutex;

#define release_lock ast_mutex_lock(&release_lock_mutex)
#define release_unlock ast_mutex_unlock(&release_lock_mutex)


char global_tracefile[BUFFERSIZE+1];


struct misdn_jb{
	int size;
	int upper_threshold;
	char *samples, *ok;
	int wp,rp;
	int state_empty;
	int state_full;
	int state_buffer;
	int bytes_wrote;
	ast_mutex_t mutexjb;
};

void export_ies(struct ast_channel *chan, struct misdn_bchannel *bc);
void import_ies(struct ast_channel *chan, struct misdn_bchannel *bc);


/* allocates the jb-structure and initialise the elements*/
struct misdn_jb *misdn_jb_init(int size, int upper_threshold);

/* frees the data and destroys the given jitterbuffer struct */
void misdn_jb_destroy(struct misdn_jb *jb);

/* fills the jitterbuffer with len data returns < 0 if there was an
error (bufferoverun). */
int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len);

/* gets len bytes out of the jitterbuffer if available, else only the
available data is returned and the return value indicates the number
of data. */
int misdn_jb_empty(struct misdn_jb *jb, char *data, int len);




enum tone_e {
	TONE_NONE=0,
	TONE_DIAL,
	TONE_ALERTING,
	TONE_BUSY,
	TONE_CUSTOM,
	TONE_FILE
};


enum misdn_chan_state {
Russell Bryant's avatar
Russell Bryant committed
	MISDN_NOTHING,		/*!< at beginning */
	MISDN_WAITING4DIGS, /*!<  when waiting for infos */
	MISDN_EXTCANTMATCH, /*!<  when asterisk couldnt match our ext */
	MISDN_DIALING, /*!<  when pbx_start */
	MISDN_PROGRESS, /*!<  we got a progress */
	MISDN_PROCEEDING, /*!<  we got a progress */
Russell Bryant's avatar
Russell Bryant committed
	MISDN_CALLING, /*!<  when misdn_call is called */
	MISDN_CALLING_ACKNOWLEDGE, /*!<  when we get SETUP_ACK */
	MISDN_ALERTING, /*!<  when Alerting */
	MISDN_BUSY, /*!<  when BUSY */
	MISDN_CONNECTED, /*!<  when connected */
Russell Bryant's avatar
Russell Bryant committed
	MISDN_BRIDGED, /*!<  when bridged */
	MISDN_CLEANING, /*!< when hangup from * but we were connected before */
	MISDN_HUNGUP_FROM_MISDN, /*!< when DISCONNECT/RELEASE/REL_COMP  cam from misdn */
	MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of */
Russell Bryant's avatar
Russell Bryant committed
	MISDN_HOLDED, /*!< if this chan is holded */
	MISDN_HOLD_DISCONNECT /*!< if this chan is holded */
  
};

#define ORG_AST 1
#define ORG_MISDN 2

struct chan_list {
  
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	ast_mutex_t lock;

	enum misdn_chan_state state;
	int holded; 
	int orginator;

	int norxtone;
	int notxtone; 

	int pipe[2];
	char ast_rd_buf[4096];
	struct ast_frame frame;

	int faxdetect;
	int faxhandled;

	int ast_dsp;
	int jb_len;
	int jb_upper_threshold;
	
	struct ast_dsp *dsp;
	struct ast_trans_pvt *trans;
  
	struct ast_channel * ast;
  
	struct misdn_bchannel *bc;
	struct misdn_bchannel *holded_bc;

	unsigned int l3id;
	int addr;

	const struct tone_zone_sound *ts;
	
	struct chan_list *peer;
	struct chan_list *next;
	struct chan_list *prev;
	struct chan_list *first;
};

struct robin_list {
	char *group;
	int port;
	int channel;
	struct robin_list *next;
	struct robin_list *prev;
};
static struct robin_list *robin = NULL;

static inline void free_robin_list_r (struct robin_list *r)
{
        if (r) {
                if (r->next) free_robin_list_r(r->next);
                if (r->group) free(r->group);
                free(r);
        }
Kevin P. Fleming's avatar
Kevin P. Fleming committed
static void free_robin_list ( void )
{
	free_robin_list_r(robin);
static struct robin_list* get_robin_position (char *group) 
{
	struct robin_list *iter = robin;
	for (; iter; iter = iter->next) {
		if (!strcasecmp(iter->group, group))
			return iter;
	}
	struct robin_list *new = (struct robin_list *)calloc(1, sizeof(struct robin_list));
	new->group = strndup(group, strlen(group));
	new->channel = 1;
	if (robin) {
		new->next = robin;
		robin->prev = new;
	}
	robin = new;
	return robin;
}


static void chan_misdn_log(int level, int port, char *tmpl, ...);

static struct ast_channel *misdn_new(struct chan_list *cl, int state,  char *exten, char *callerid, int format, int port, int c);
static void send_digit_to_chan(struct chan_list *cl, char digit );


#define AST_CID_P(ast) ast->cid.cid_num
#define AST_BRIDGED_P(ast) ast_bridged_channel(ast) 
#define AST_LOAD_CFG ast_config_load
#define AST_DESTROY_CFG ast_config_destroy

Kevin P. Fleming's avatar
Kevin P. Fleming committed
#define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt
#define MISDN_ASTERISK_PVT(ast) 1

#include <asterisk/strings.h>

/* #define MISDN_DEBUG 1 */

static char *desc = "Channel driver for mISDN Support (Bri/Pri)";
static const char misdn_type[] = "mISDN";
static char **misdn_key_vector=NULL;
static int misdn_key_vector_size=0;

/* Only alaw and mulaw is allowed for now */
static int prefformat =  AST_FORMAT_ALAW ; /*  AST_FORMAT_SLINEAR ;  AST_FORMAT_ULAW | */

static ast_mutex_t usecnt_lock; 

static int *misdn_debug;
static int *misdn_debug_only;
static int max_ports;

struct chan_list dummy_cl;

struct chan_list *cl_te=NULL;
Kevin P. Fleming's avatar
Kevin P. Fleming committed
ast_mutex_t cl_te_lock;
cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data);

static void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel*bc);

static void cl_queue_chan(struct chan_list **list, struct chan_list *chan);
static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan);
static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc);



static int tone_indicate( struct chan_list *cl, enum tone_e tone);

static int start_bc_tones(struct chan_list *cl);
static int stop_bc_tones(struct chan_list *cl);
static void release_chan(struct misdn_bchannel *bc);

static int misdn_set_opt_exec(struct ast_channel *chan, void *data);
static int misdn_facility_exec(struct ast_channel *chan, void *data);

int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len);

/*************** Helpers *****************/

static struct chan_list * get_chan_by_ast(struct ast_channel *ast)
{
	struct chan_list *tmp;
  
	for (tmp=cl_te; tmp; tmp = tmp->next) {
		if ( tmp->ast == ast ) return tmp;
	}
  
	return NULL;
}

static struct chan_list * get_chan_by_ast_name(char *name)
{
	struct chan_list *tmp;
  
	for (tmp=cl_te; tmp; tmp = tmp->next) {
		if ( tmp->ast  && strcmp(tmp->ast->name,name) == 0) return tmp;
	}
  
	return NULL;
}


static char *bearer2str(int cap) {
	static char *bearers[]={
		"Speech",
		"Audio 3.1k",
		"Unres Digital",
		"Res Digital",
		"Unknown Bearer"
	};
	
	switch (cap) {
	case INFO_CAPABILITY_SPEECH:
		return bearers[0];
		break;
	case INFO_CAPABILITY_AUDIO_3_1K:
		return bearers[1];
		break;
	case INFO_CAPABILITY_DIGITAL_UNRESTRICTED:
		return bearers[2];
		break;
	case INFO_CAPABILITY_DIGITAL_RESTRICTED:
		return bearers[3];
		break;
	default:
		return bearers[4];
		break;
	}
}


static void print_facility( struct misdn_bchannel *bc)
{
	switch (bc->fac_type) {
	case FACILITY_CALLDEFLECT:
		chan_misdn_log(2,bc->port," --> calldeflect: %s\n",
			       bc->fac.calldeflect_nr);
		break;
	case FACILITY_CENTREX:
		chan_misdn_log(2,bc->port," --> centrex: %s\n",
			       bc->fac.cnip);
		break;
	default:
		chan_misdn_log(2,bc->port," --> unknown\n");
		
	}
}

static void print_bearer(struct misdn_bchannel *bc) 
{
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	
	chan_misdn_log(2, bc->port, " --> Bearer: %s\n",bearer2str(bc->capability));
	
	switch(bc->law) {
	case INFO_CODEC_ALAW:
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		chan_misdn_log(2, bc->port, " --> Codec: Alaw\n");
		break;
	case INFO_CODEC_ULAW:
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		chan_misdn_log(2, bc->port, " --> Codec: Ulaw\n");
		break;
	}
}
/*************** Helpers END *************/

static void send_digit_to_chan(struct chan_list *cl, char digit )
{
	static const char* dtmf_tones[] = {
		"!941+1336/100,!0/100",	/* 0 */
		"!697+1209/100,!0/100",	/* 1 */
		"!697+1336/100,!0/100",	/* 2 */
		"!697+1477/100,!0/100",	/* 3 */
		"!770+1209/100,!0/100",	/* 4 */
		"!770+1336/100,!0/100",	/* 5 */
		"!770+1477/100,!0/100",	/* 6 */
		"!852+1209/100,!0/100",	/* 7 */
		"!852+1336/100,!0/100",	/* 8 */
		"!852+1477/100,!0/100",	/* 9 */
		"!697+1633/100,!0/100",	/* A */
		"!770+1633/100,!0/100",	/* B */
		"!852+1633/100,!0/100",	/* C */
		"!941+1633/100,!0/100",	/* D */
		"!941+1209/100,!0/100",	/* * */
		"!941+1477/100,!0/100" };	/* # */
	struct ast_channel *chan=cl->ast; 
  
	if (digit >= '0' && digit <='9')
		ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
	else if (digit >= 'A' && digit <= 'D')
		ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0);
	else if (digit == '*')
		ast_playtones_start(chan,0,dtmf_tones[14], 0);
	else if (digit == '#')
		ast_playtones_start(chan,0,dtmf_tones[15], 0);
	else {
		/* not handled */
		ast_log(LOG_DEBUG, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name);
    
    
	}
}
/*** CLI HANDLING ***/
static int misdn_set_debug(int fd, int argc, char *argv[])
{
	if (argc != 4 && argc != 5 && argc != 6 && argc != 7)
		return RESULT_SHOWUSAGE; 

	int level = atoi(argv[3]);

	switch (argc) {
		case 4:	
		case 5: {
					int only = 0;
					if (argc == 5) {
						if (strncasecmp(argv[4], "only", strlen(argv[4])))
							return RESULT_SHOWUSAGE;
						else
							only = 1;
					}
					int i;
					for (i=0; i<=max_ports; i++) {
						misdn_debug[i] = level;
						misdn_debug_only[i] = only;
					}
					ast_cli(fd, "changing debug level for all ports to %d%s\n",misdn_debug[0], only?" (only)":"");
				}
				break;
		case 6: 
		case 7: {
					if (strncasecmp(argv[4], "port", strlen(argv[4])))
						return RESULT_SHOWUSAGE;
					int port = atoi(argv[5]);
					if (port <= 0 || port > max_ports) {
						switch (max_ports) {
							case 0:
								ast_cli(fd, "port number not valid! no ports available so you won't get lucky with any number here...\n");
								break;
							case 1:
								ast_cli(fd, "port number not valid! only port 1 is availble.\n");
								break;
							default:
								ast_cli(fd, "port number not valid! only ports 1 to %d are available.\n", max_ports);
							}
							return 0;
					}
					if (argc == 7) {
						if (strncasecmp(argv[6], "only", strlen(argv[6])))
							return RESULT_SHOWUSAGE;
						else
							misdn_debug_only[port] = 1;
					} else
						misdn_debug_only[port] = 0;
					misdn_debug[port] = level;
					ast_cli(fd, "changing debug level to %d%s for port %d\n", misdn_debug[port], misdn_debug_only[port]?" (only)":"", port);
				}
	}
	return 0;
}

static int misdn_set_crypt_debug(int fd, int argc, char *argv[])
{

	return 0;
}


static int misdn_restart_port (int fd, int argc, char *argv[])
{
	int port;
  
	if (argc != 4)
		return RESULT_SHOWUSAGE;
  
	port = atoi(argv[3]);

	misdn_lib_port_restart(port);

	return 0;
}

static int misdn_port_up (int fd, int argc, char *argv[])
{
	int port;
	
	if (argc != 4)
		return RESULT_SHOWUSAGE;
	
	port = atoi(argv[3]);
	
	misdn_lib_get_port_up(port);
  
	return 0;
}

static int misdn_port_down (int fd, int argc, char *argv[])
{
	int port;
	
	if (argc != 4)
		return RESULT_SHOWUSAGE;
	
	port = atoi(argv[3]);
	
	misdn_lib_get_port_down(port);
  
	return 0;
}


static int misdn_show_config (int fd, int argc, char *argv[])
{
	char buffer[BUFFERSIZE];
	enum misdn_cfg_elements elem;
	int linebreak;

	int onlyport = -1;
	if (argc >= 4) {
		if (!sscanf(argv[3], "%d", &onlyport) || onlyport < 0) {
			ast_cli(fd, "Unknown option: %s\n", argv[3]);
			return RESULT_SHOWUSAGE;
		}
	}
	
	if (argc == 3 || onlyport == 0) {
		ast_cli(fd,"Misdn General-Config: \n"); 
		ast_cli(fd," -> Version: chan_misdn-" CHAN_MISDN_VERSION "\n");
		for (elem = MISDN_GEN_FIRST + 1, linebreak = 1; elem < MISDN_GEN_LAST; elem++, linebreak++) {
			misdn_cfg_get_config_string( 0, elem, buffer, BUFFERSIZE);
			ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
		}
	}

	if (onlyport < 0) {
		int port = misdn_cfg_get_next_port(0);
		for (; port > 0; port = misdn_cfg_get_next_port(port)) {
			ast_cli(fd, "\n[PORT %d]\n", port);
			for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) {
				misdn_cfg_get_config_string( port, elem, buffer, BUFFERSIZE);
				ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
			}	
			ast_cli(fd, "\n");
		}
	}
	
	if (onlyport > 0) {
		if (misdn_cfg_is_port_valid(onlyport)) {
			ast_cli(fd, "[PORT %d]\n", onlyport);
			for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) {
				misdn_cfg_get_config_string( onlyport, elem, buffer, BUFFERSIZE);
				ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : "");
			}	
			ast_cli(fd, "\n");
		} else {
			ast_cli(fd, "Port %d is not active!\n", onlyport);
		}
	}
	return 0;
}

struct state_struct {
	enum misdn_chan_state state;
	char txt[255] ;
} ;

static struct state_struct state_array[] = {
	{MISDN_NOTHING,"NOTHING"}, /* at beginning */
	{MISDN_WAITING4DIGS,"WAITING4DIGS"}, /*  when waiting for infos */
	{MISDN_EXTCANTMATCH,"EXTCANTMATCH"}, /*  when asterisk couldnt match our ext */
	{MISDN_DIALING,"DIALING"}, /*  when pbx_start */
	{MISDN_PROGRESS,"PROGRESS"}, /*  when pbx_start */
	{MISDN_CALLING,"CALLING"}, /*  when misdn_call is called */
	{MISDN_ALERTING,"ALERTING"}, /*  when Alerting */
	{MISDN_BUSY,"BUSY"}, /*  when BUSY */
	{MISDN_CONNECTED,"CONNECTED"}, /*  when connected */
	{MISDN_BRIDGED,"BRIDGED"}, /*  when bridged */
	{MISDN_CLEANING,"CLEANING"}, /* when hangup from * but we were connected before */
	{MISDN_HUNGUP_FROM_MISDN,"HUNGUP_FROM_MISDN"}, /* when DISCONNECT/RELEASE/REL_COMP  cam from misdn */
	{MISDN_HOLDED,"HOLDED"}, /* when DISCONNECT/RELEASE/REL_COMP  cam from misdn */
	{MISDN_HOLD_DISCONNECT,"HOLD_DISCONNECT"}, /* when DISCONNECT/RELEASE/REL_COMP  cam from misdn */
	{MISDN_HUNGUP_FROM_AST,"HUNGUP_FROM_AST"} /* when DISCONNECT/RELEASE/REL_COMP came out of */
	/* misdn_hangup */
};

static char *misdn_get_ch_state(struct chan_list *p) 
{
	int i;
	if( !p) return NULL;
  
	for (i=0; i< sizeof(state_array)/sizeof(struct state_struct); i++) {
		if ( state_array[i].state == p->state) return state_array[i].txt; 
	}
  
	return NULL;
}



void reload_config(void)
	chan_misdn_log(-1, 0, "Dynamic Crypting Activation is not support during reload at the moment\n");
	
	free_robin_list();
	misdn_cfg_reload();
	misdn_cfg_update_ptp();
	misdn_cfg_get( 0, MISDN_GEN_TRACEFILE, global_tracefile, BUFFERSIZE);
	misdn_cfg_get( 0, MISDN_GEN_DEBUG, &cfg_debug, sizeof(int));
	for (i = 0;  i <= max_ports; i++) {
		misdn_debug[i] = cfg_debug;
		misdn_debug_only[i] = 0;
	}
}

static int misdn_reload (int fd, int argc, char *argv[])
{
	ast_cli(fd, "Reloading mISDN Config\n");
	reload_config();
	return 0;
}

static void print_bc_info (int fd, struct chan_list* help, struct misdn_bchannel* bc)
{
	struct ast_channel *ast=help->ast;
	ast_cli(fd,
		"* Pid:%d Prt:%d Ch:%d Mode:%s Org:%s dad:%s oad:%s rad:%s ctx:%s state:%s\n",
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		bc->pid, bc->port, bc->channel,
		bc->nt?"NT":"TE",
		help->orginator == ORG_AST?"*":"I",
		ast?ast->exten:NULL,
		ast?AST_CID_P(ast):NULL,
		ast?ast->context:NULL,
		misdn_get_ch_state(help)
		);
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	if (misdn_debug[bc->port] > 0)
		ast_cli(fd,
			"  --> astname: %s\n"
			"  --> ch_l3id: %x\n"
			"  --> ch_addr: %x\n"
			"  --> bc_addr: %x\n"
			"  --> bc_l3id: %x\n"
			"  --> display: %s\n"
			"  --> activated: %d\n"
			"  --> state: %s\n"
			"  --> capability: %s\n"
			"  --> echo_cancel: %d\n"
			"  --> notone : rx %d tx:%d\n"
			"  --> bc_hold: %d holded_bc :%d\n",
			help->ast->name,
			help->l3id,
			help->addr,
			bc->addr,
			bc?bc->l3_id:-1,
			bc->display,
			
			bc->active,
			bc_state2str(bc->bc_state),
			bearer2str(bc->capability),
			bc->ec_enable,
			help->norxtone,help->notxtone,
			bc->holded, help->holded_bc?1:0
			);
  
}

static int misdn_show_cls (int fd, int argc, char *argv[])
{
	struct chan_list *help=cl_te;
  
	ast_cli(fd,"Chan List: %p\n",cl_te); 
  
	for (;help; help=help->next) {
		struct misdn_bchannel *bc=help->bc;   
		struct ast_channel *ast=help->ast;
		if (misdn_debug[0] > 2) ast_cli(fd, "Bc:%p Ast:%p\n", bc, ast);
		if (bc) {
			print_bc_info(fd, help, bc);
		} else if ( (bc=help->holded_bc) ) {
			chan_misdn_log(0, 0, "ITS A HOLDED BC:\n");
			print_bc_info(fd, help,  bc);
		} else {
			ast_cli(fd,"* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, AST_CID_P(ast));
		}
	}
  
  
	return 0;
}

static int misdn_show_cl (int fd, int argc, char *argv[])
{
	struct chan_list *help=cl_te;

	if (argc != 4)
		return RESULT_SHOWUSAGE;
  
	for (;help; help=help->next) {
		struct misdn_bchannel *bc=help->bc;   
		struct ast_channel *ast=help->ast;
    
		if (bc && ast) {
			if (!strcasecmp(ast->name,argv[3])) {
				print_bc_info(fd, help, bc);
				break; 
			}
		} 
	}
  
  
	return 0;
}

Kevin P. Fleming's avatar
Kevin P. Fleming committed
ast_mutex_t lock;
int MAXTICS=8;

static int misdn_set_tics (int fd, int argc, char *argv[])
{
	if (argc != 4)
		return RESULT_SHOWUSAGE;
  
	MAXTICS=atoi(argv[3]);
  
	return 0;
}

static int misdn_show_stacks (int fd, int argc, char *argv[])
{
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	int port;
	
	ast_cli(fd, "BEGIN STACK_LIST:\n");
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	for (port=misdn_cfg_get_next_port(0); port > 0;
	     port=misdn_cfg_get_next_port(port)) {
		char buf[128];
		get_show_stack_details(port,buf);
		ast_cli(fd,"  %s  Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port]?"(only)":"");
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		

	return 0;

}

static int misdn_show_port (int fd, int argc, char *argv[])
{
	int port;
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	
	if (argc != 4)
		return RESULT_SHOWUSAGE;
  
	port = atoi(argv[3]);
  
	ast_cli(fd, "BEGIN STACK_LIST:\n");

Kevin P. Fleming's avatar
Kevin P. Fleming committed
	char buf[128];
	get_show_stack_details(port,buf);
	ast_cli(fd,"  %s  Debug:%d%s\n",buf, misdn_debug[port], misdn_debug_only[port]?"(only)":"");

	
	return 0;
}

static int misdn_send_cd (int fd, int argc, char *argv[])
{
	char *channame; 
	char *nr; 
  
	if (argc != 5)
		return RESULT_SHOWUSAGE;
  
	channame = argv[3];
	nr = argv[4];
	
	ast_cli(fd, "Sending Calldeflection (%s) to %s\n",nr, channame);
	
	{
		struct chan_list *tmp=get_chan_by_ast_name(channame);
		
		if (!tmp) {
			ast_cli(fd, "Sending CD with nr %s to %s failed Channel does not exist\n",nr, channame);
			return 0; 
		} else {
			
			misdn_lib_send_facility(tmp->bc, FACILITY_CALLDEFLECT, nr);
		}
	}
  
	return 0; 
}

static int misdn_send_digit (int fd, int argc, char *argv[])
{
	char *channame; 
	char *msg; 
  
	if (argc != 5)
		return RESULT_SHOWUSAGE;
  
	channame = argv[3];
	msg = argv[4];

	ast_cli(fd, "Sending %s to %s\n",msg, channame);
  
	{
		struct chan_list *tmp=get_chan_by_ast_name(channame);
    
		if (!tmp) {
			ast_cli(fd, "Sending %s to %s failed Channel does not exist\n",msg, channame);
			return 0; 
		} else {
#if 1
			int i;
			int msglen = strlen(msg);
			for (i=0; i<msglen; i++) {
				ast_cli(fd, "Sending: %c\n",msg[i]);
				send_digit_to_chan(tmp, msg[i]);
				/* res = ast_safe_sleep(tmp->ast, 250); */
				usleep(250000);
				/* res = ast_waitfor(tmp->ast,100); */
			}
#else
			int res;
			res = ast_dtmf_stream(tmp->ast,NULL,msg,250);
#endif
		}
	}
  
	return 0; 
}

static int misdn_toggle_echocancel (int fd, int argc, char *argv[])
{
	char *channame; 

	if (argc != 4)
		return RESULT_SHOWUSAGE;
	
	channame = argv[3];
  
	ast_cli(fd, "Toggling EchoCancel on %s\n", channame);
  
	{
		struct chan_list *tmp=get_chan_by_ast_name(channame);
    
		if (!tmp) {
			ast_cli(fd, "Toggling EchoCancel %s failed Channel does not exist\n", channame);
			return 0; 
		} else {
			tmp->bc->ec_enable=tmp->bc->ec_enable?0:1;

			if (tmp->bc->ec_enable) {
				manager_ec_enable(tmp->bc);
			} else {
				manager_ec_disable(tmp->bc);
			}
		}
	}
  
	return 0; 
}

static int misdn_send_display (int fd, int argc, char *argv[])
{
	char *channame; 
	char *msg; 
  
	if (argc != 5)
		return RESULT_SHOWUSAGE;
  
	channame = argv[3];
	msg = argv[4];

	ast_cli(fd, "Sending %s to %s\n",msg, channame);
	{
		struct chan_list *tmp;
		tmp=get_chan_by_ast_name(channame);
    
		if (tmp && tmp->bc) {
			ast_copy_string(tmp->bc->display, msg, sizeof(tmp->bc->display));
			misdn_lib_send_event(tmp->bc, EVENT_INFORMATION);
		} else {
			ast_cli(fd,"No such channel %s\n",channame);
			return RESULT_FAILURE;
		}
	}

	return RESULT_SUCCESS ;
}

static char *complete_ch_helper(const char *line, const char *word, int pos, int state, int rpos)
{
	struct ast_channel *c;
	int which=0;
	char *ret;
	if (pos != rpos)
		return NULL;
	c = ast_channel_walk_locked(NULL);
	while(c) {
		if (!strncasecmp(word, c->name, strlen(word))) {
			if (++which > state)
				break;
		}
		ast_mutex_unlock(&c->lock);
		c = ast_channel_walk_locked(c);
	}
	if (c) {
		ret = strdup(c->name);
		ast_mutex_unlock(&c->lock);
	} else
		ret = NULL;
	return ret;
}

static char *complete_ch(const char *line, const char *word, int pos, int state)
{
	return complete_ch_helper(line, word, pos, state, 3);
}

static char *complete_debug_port (const char *line, const char *word, int pos, int state)
{
	if (state)
		return NULL;

	switch (pos) {
	case 4: if (*word == 'p')
				return strdup("port");
			else if (*word == 'o')
				return strdup("only");
			break;
	case 6: if (*word == 'o')
				return strdup("only");
			break;
	}
	return NULL;
}

static struct ast_cli_entry cli_send_cd =
{ {"misdn","send","calldeflect", NULL},
  misdn_send_cd,
  "Sends CallDeflection to mISDN Channel", 
  "Usage: misdn send calldeflect <channel> \"<nr>\" \n",
  complete_ch
};

static struct ast_cli_entry cli_send_digit =
{ {"misdn","send","digit", NULL},
  misdn_send_digit,
  "Sends DTMF Digit to mISDN Channel", 
  "Usage: misdn send digit <channel> \"<msg>\" \n"
  "       Send <digit> to <channel> as DTMF Tone\n"
  "       when channel is a mISDN channel\n",
  complete_ch
};

static struct ast_cli_entry cli_toggle_echocancel =
{ {"misdn","toggle","echocancel", NULL},
  misdn_toggle_echocancel,
  "Toggles EchoCancel on mISDN Channel", 
  "Usage: misdn toggle echocancel <channel>\n", 
  complete_ch
};

static struct ast_cli_entry cli_send_display =
{ {"misdn","send","display", NULL},
  misdn_send_display,
  "Sends Text to mISDN Channel", 
  "Usage: misdn send display <channel> \"<msg>\" \n"
  "       Send <msg> to <channel> as Display Message\n"
  "       when channel is a mISDN channel\n",
  complete_ch
};

static struct ast_cli_entry cli_show_config =