Skip to content
Snippets Groups Projects
chan_misdn.c 85.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Russell Bryant's avatar
    Russell Bryant committed
     * Asterisk -- An open source telephony toolkit.
     * 
    
     * Copyright (C) 2004, 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 "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)
    
    
    /* BEGIN: chan_misdn.h */
    
    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_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 */
    	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;
    	
    	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;
    	
    	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);
    	}
    }
    
    static void free_robin_list ()
    {
    	free_robin_list_r(robin);
    }
    
    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;
    }
    
    struct ast_channel *misdn_new(struct chan_list *cl, int state, char * name, char * context, char *exten, char *callerid, int format, int port, int c);
    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
    #define MISDN_ASTERISK_TYPE(ast) ast->tech->type
    
    
    /* END: chan_misdn.h */
    
    #include <asterisk/strings.h>
    
    /* #define MISDN_DEBUG 1 */
    
    static  char *desc = "Channel driver for mISDN Support (Bri/Pri)";
    static  char *type = "mISDN";
    
    int tracing = 0 ;
    
    static int usecnt=0;
    
    char **misdn_key_vector=NULL;
    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; 
    
    int *misdn_debug;
    int *misdn_debug_only;
    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;
    
    
    enum event_response_e
    cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data);
    
    void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel*bc);
    
    void cl_queue_chan(struct chan_list **list, struct chan_list *chan);
    void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan);
    struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc);
    void chan_misdn_log(int level, int port, char *tmpl, ...);
    void chan_misdn_trace_call(struct ast_channel *chan, int debug, char *tmpl, ...);
    
    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);
    
    /*************** 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* tone2str(struct misdn_bchannel *bc)
    {
    	static struct {
    		char name[16];
    		enum tone_e tone;
    	} *tone, tone_buf[] = {
    		{"NOTONE",TONE_NONE},
    		{"DIAL",TONE_DIAL},
    		{"BUSY",TONE_BUSY},
    		{"ALERT",TONE_ALERTING},
    		{"",TONE_NONE}
    	};
      
      
    	for (tone=&tone_buf[0]; tone->name[0]; tone++) {
    		if (tone->tone == bc->tone) return tone->name;
    	}
    	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_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 *************/
    
    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[])
    {
    	if (argc != 5 )return RESULT_SHOWUSAGE; 
    
    	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_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_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] ;
    } ;
    
    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 */
    };
    
    
    
    
    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;
    }
    
    static int misdn_reload (int fd, int argc, char *argv[])
    {
    	int i, cfg_debug;
    	
    	ast_cli(fd, "Reloading mISDN Config\n");
    	chan_misdn_log(0, 0, "Dynamic Crypting Activation is not support during reload at the moment\n");
    	
    	free_robin_list();
    
    	misdn_cfg_reload();
    
    	{
    		char tempbuf[BUFFERSIZE];
    		misdn_cfg_get( 0, MISDN_GEN_TRACEFILE, tempbuf, BUFFERSIZE);
    		if (strlen(tempbuf))
    			tracing = 1;
    	}
    
    	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;
    	}
    	
    	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 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"
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			"  --> tone: %s\n"
    
    			"  --> display: %s\n"
    			"  --> activated: %d\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,
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			tone2str(bc),
    
    			bc->display,
    			
    			bc->active,
    			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) {
    			int l = sizeof(tmp->bc->display);
    			strncpy(tmp->bc->display, msg, l);
    			tmp->bc->display[l-1] = 0;
    			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(char *line, 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(char *line, char *word, int pos, int state)
    {
    	return complete_ch_helper(line, word, pos, state, 3);
    }
    
    static char *complete_debug_port (char *line, 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 =
    { {"misdn","show","config", NULL},
      misdn_show_config,
      "Shows internal mISDN config, read from cfg-file", 
      "Usage: misdn show config [port | 0]\n       use 0 to only print the general config.\n"
    };
     
    
    static struct ast_cli_entry cli_reload =
    { {"misdn","reload", NULL},
      misdn_reload,
      "Reloads internal mISDN config, read from cfg-file", 
      "Usage: misdn reload\n"
    };
    
    static struct ast_cli_entry cli_set_tics =
    { {"misdn","set","tics", NULL},
      misdn_set_tics,
      "", 
      "\n"
    };
    
    
    static struct ast_cli_entry cli_show_cls =
    { {"misdn","show","channels", NULL},
      misdn_show_cls,
      "Shows internal mISDN chan_list", 
      "Usage: misdn show channels\n"
    };
    
    static struct ast_cli_entry cli_show_cl =
    { {"misdn","show","channel", NULL},
      misdn_show_cl,
      "Shows internal mISDN chan_list", 
      "Usage: misdn show channels\n",
      complete_ch
    };
    
    
    
    static struct ast_cli_entry cli_restart_port =
    { {"misdn","restart","port", NULL},
      misdn_restart_port,
      "Restarts the given port", 
      "Usage: misdn restart port\n"
    };
    
    
    static struct ast_cli_entry cli_port_up =
    { {"misdn","port","up", NULL},
      misdn_port_up,
      "Tries to establish L1 on the given port", 
      "Usage: misdn port up <port>\n"
    };
    
    
    static struct ast_cli_entry cli_show_stacks =