Skip to content
Snippets Groups Projects
chan_misdn.c 104 KiB
Newer Older
  • Learn to ignore specific revisions
  •       
    			p->state=MISDN_CLEANING;
    			misdn_lib_send_event( bc, EVENT_DISCONNECT);
    			break;
    		case MISDN_CONNECTED:
    			/*  Alerting or Disconect */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			chan_misdn_log(2, bc->port, " --> * State Connected\n");
    
    			tone_indicate(p, TONE_BUSY);
    
    			misdn_lib_send_event( bc, EVENT_DISCONNECT);
          
    			p->state=MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */
    			break;
    
    		case MISDN_DISCONNECTED:
    			chan_misdn_log(2, bc->port, " --> * State Disconnected\n");
    			misdn_lib_send_event( bc, EVENT_RELEASE);
    			p->state=MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */
    			break;
    
    
    		case MISDN_CLEANING:
    			break;
          
    		case MISDN_HOLD_DISCONNECT:
    			/* need to send release here */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			chan_misdn_log(2, bc->port, " --> state HOLD_DISC\n");
    			chan_misdn_log(1, bc->port, " --> cause %d\n",bc->cause);
    			chan_misdn_log(1, bc->port, " --> out_cause %d\n",bc->out_cause);
    
    			misdn_lib_send_event(bc,EVENT_RELEASE);
    			break;
    		default:
    			/*  Alerting or Disconect */
    
    				misdn_lib_send_event(bc, EVENT_RELEASE);
    
    				misdn_lib_send_event(bc, EVENT_DISCONNECT);
    			p->state=MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */
    		}
        
    	}
    	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	chan_misdn_log(1, bc->port, "Channel: %s hanguped\n",ast->name);
    
    static struct ast_frame  *misdn_read(struct ast_channel *ast)
    
    {
    	struct chan_list *tmp;
    	
    	char blah[255];
    	int len =0 ;
    	
    	if (!ast) return NULL;
    
    	if (! (tmp=MISDN_ASTERISK_TECH_PVT(ast)) ) return NULL;
    
    	if (!tmp->bc) return NULL;
    	
    	
    	read(tmp->pipe[0],blah,sizeof(blah));
    	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	
    	len = misdn_ibuf_usedcount(tmp->bc->astbuf);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    	if (!len) {
    		chan_misdn_log(4,tmp->bc->port,"misdn_read: ZERO READ\n");
    
    		tmp->frame.frametype = AST_FRAME_NULL;
    		tmp->frame.subclass = 0;
    		return &tmp->frame;
    	}
    
    
    	/*shrinken len if necessary, we transmit at maximum 4k*/
    	len = len<=sizeof(tmp->ast_rd_buf)?len:sizeof(tmp->ast_rd_buf);
    	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	misdn_ibuf_memcpy_r(tmp->ast_rd_buf, tmp->bc->astbuf,len);
    
    	
    	tmp->frame.frametype  = AST_FRAME_VOICE;
    	tmp->frame.subclass = AST_FORMAT_ALAW;
    	tmp->frame.datalen = len;
    	tmp->frame.samples = len ;
    	tmp->frame.mallocd =0 ;
    	tmp->frame.offset= 0 ;
    	tmp->frame.src = NULL;
    	tmp->frame.data = tmp->ast_rd_buf ;
    
    	return &tmp->frame;
    }
    
    
    
    
    
    static int misdn_write(struct ast_channel *ast, struct ast_frame *frame)
    
    	if (!ast || ! (ch=MISDN_ASTERISK_TECH_PVT(ast)) ) return -1;
    
    		ast_log(LOG_WARNING, "private but no bc\n");
    		return -1;
    	}
    	
    
    	/*if (ch->bc->tone != TONE_NONE)
    	  tone_indicate(ch,TONE_NONE); */
    
    	if (ch->holded ) {
    		chan_misdn_log(5, ch->bc->port, "misdn_write: Returning because holded\n");
    
    	if (ch->notxtone) {
    		chan_misdn_log(5, ch->bc->port, "misdn_write: Returning because notxone\n");
    
    		chan_misdn_log(4, ch->bc->port, "misdn_write: * prods us\n");
    
    	
    	if ( !(frame->subclass & prefformat)) {
    
    		chan_misdn_log(-1, ch->bc->port, "Got Unsupported Frame with Format:%d\n", frame->subclass);
    
    
    	if ( !frame->samples ) {
    		chan_misdn_log(4, ch->bc->port, "misdn_write: zero write\n");
    		return 0;
    	}
    
    	if ( ! ch->bc->addr ) {
    
    		chan_misdn_log(8, ch->bc->port, "misdn_write: no addr for bc dropping:%d\n", frame->samples);
    
    	
    #if MISDN_DEBUG
    	{
    		int i, max=5>frame->samples?frame->samples:5;
    		
    		printf("write2mISDN %p %d bytes: ", p, frame->samples);
    		
    		for (i=0; i<  max ; i++) printf("%2.2x ",((char*) frame->data)[i]);
    		printf ("\n");
    	}
    #endif
    
    
    	switch (ch->bc->bc_state) {
    		case BCHAN_ACTIVATED:
    		case BCHAN_BRIDGED:
    			break;
    		default:
    
    		chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) droping: %d frames addr:%x exten:%s cid:%s ch->state:%s\n",frame->samples,ch->bc->addr, ast->exten, ast->cid.cid_num,misdn_get_ch_state( ch));
    
    		return 0;
    	}
    	
    	chan_misdn_log(9, ch->bc->port, "Sending :%d bytes 2 MISDN\n",frame->samples);
    
    	/*if speech flip bits*/
    	if ( misdn_cap_is_speech(ch->bc->capability) )
    		flip_buf_bits(frame->data,frame->samples);
    	
    	
    	if ( !ch->bc->nojitter && misdn_cap_is_speech(ch->bc->capability) ) {
    		/* Buffered Transmit (triggert by read from isdn side)*/
    		if (misdn_jb_fill(ch->jb,frame->data,frame->samples) < 0) {
    			if (ch->bc->active)
    				cb_log(0,ch->bc->port,"Misdn Jitterbuffer Overflow.\n");
    		}
    		
    	} else {
    		/*transmit without jitterbuffer*/
    		i=misdn_lib_tx2misdn_frm(ch->bc, frame->data, frame->samples);
    	}
    
    	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    enum ast_bridge_result  misdn_bridge (struct ast_channel *c0,
    				      struct ast_channel *c1, int flags,
    				      struct ast_frame **fo,
    				      struct ast_channel **rc,
    				      int timeoutms)
    
    
    {
    	struct chan_list *ch1,*ch2;
    	struct ast_channel *carr[2], *who;
    	int to=-1;
    	struct ast_frame *f;
      
    	ch1=get_chan_by_ast(c0);
    	ch2=get_chan_by_ast(c1);
    
    	carr[0]=c0;
    	carr[1]=c1;
      
      
    	if (ch1 && ch2 ) ;
    	else
    		return -1;
      
    
    	int bridging;
    	misdn_cfg_get( 0, MISDN_GEN_BRIDGING, &bridging, sizeof(int));
    	if (bridging) {
    		int ecwb;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		misdn_cfg_get( ch1->bc->port, MISDN_CFG_ECHOCANCELWHENBRIDGED, &ecwb, sizeof(int));
    
    			chan_misdn_log(2, ch1->bc->port, "Disabling Echo Cancellor when Bridged\n");
    
    		/*	manager_ec_disable(ch1->bc); */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		misdn_cfg_get( ch2->bc->port, MISDN_CFG_ECHOCANCELWHENBRIDGED, &ecwb, sizeof(int));
    
    			chan_misdn_log(2, ch2->bc->port, "Disabling Echo Cancellor when Bridged\n");
    
    		/*	manager_ec_disable(ch2->bc); */
    
    		}
    		
    		/* trying to make a mISDN_dsp conference */
    
    		chan_misdn_log(1, ch1->bc->port, "I SEND: Making conference with Number:%d\n", (ch1->bc->pid<<1) +1);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		misdn_lib_bridge(ch1->bc,ch2->bc);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	
    
    	if (option_verbose > 2) 
    		ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	chan_misdn_log(1, ch1->bc->port, "* Makeing Native Bridge between %s and %s\n", ch1->bc->oad, ch2->bc->oad);
    
      
    	while(1) {
    		to=-1;
    		who = ast_waitfor_n(carr, 2, &to);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    		if (!who) {
    
    Christian Richter's avatar
     
    Christian Richter committed
    			ast_log(LOG_NOTICE,"misdn_bridge: empty read, breaking out\n");
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		}
    
    		f = ast_read(who);
        
    		if (!f || f->frametype == AST_FRAME_CONTROL) {
    			/* got hangup .. */
    			*fo=f;
    			*rc=who;
          
    			break;
    		}
        
        
    		if (who == c0) {
    			ast_write(c1,f);
    		}
    		else {
    			ast_write(c0,f);
    		}
        
    	}
      
    	if (bridging) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		misdn_lib_split_bridge(ch1->bc,ch2->bc);
    
    static int tone_indicate( struct chan_list *cl, enum tone_e tone)
    {
    	const struct tone_zone_sound *ts= NULL;
    	struct ast_channel *ast=cl->ast;
    	
    
    	chan_misdn_log(2,cl->bc->port,"Tone Indicate:\n");
    
    	
    	if (!cl->ast) {
    		return 0;
    	}
    	
    	switch (tone) {
    	case TONE_DIAL:
    
    		chan_misdn_log(2,cl->bc->port," --> Dial\n");
    
    		ts=ast_get_indication_tone(ast->zone,"dial");
    		misdn_lib_tone_generator_start(cl->bc);
    		break;
    	case TONE_ALERTING:
    
    		chan_misdn_log(2,cl->bc->port," --> Ring\n");
    
    		ts=ast_get_indication_tone(ast->zone,"ring");
    		misdn_lib_tone_generator_stop(cl->bc);
    		break;
    	case TONE_BUSY:
    
    		chan_misdn_log(2,cl->bc->port," --> Busy\n");
    
    		ts=ast_get_indication_tone(ast->zone,"busy");
    		misdn_lib_tone_generator_stop(cl->bc);
    		break;
    	case TONE_FILE:
    		break;
    
    	case TONE_NONE:
    
    		chan_misdn_log(2,cl->bc->port," --> None\n");
    
    		misdn_lib_tone_generator_stop(cl->bc);
    		ast_playtones_stop(ast);
    		break;
    	default:
    		chan_misdn_log(0,cl->bc->port,"Don't know how to handle tone: %d\n",tone);
    	}
    	
    	cl->ts=ts;	
    	
    	if (ts) ast_playtones_start(ast,0, ts->data, 0);
    	
    	return 0;
    }
    
    
    static int start_bc_tones(struct chan_list* cl)
    {
    	manager_bchannel_activate(cl->bc);
    
    	misdn_lib_tone_generator_stop(cl->bc);
    
    	cl->notxtone=0;
    	cl->norxtone=0;
    	return 0;
    }
    
    static int stop_bc_tones(struct chan_list *cl)
    {
    	if (cl->bc) {
    		manager_bchannel_deactivate(cl->bc);
    	}
    	cl->notxtone=1;
    	cl->norxtone=1;
    	
    	return 0;
    }
    
    
    
    static struct chan_list *init_chan_list(void)
    
    {
    	struct chan_list *cl=malloc(sizeof(struct chan_list));
    	
    	if (!cl) {
    
    		chan_misdn_log(-1, 0, "misdn_request: malloc failed!");
    
    		return NULL;
    	}
    	
    	memset(cl,0,sizeof(struct chan_list));
    	
    	return cl;
    	
    }
    
    static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause)
    
    {
    	struct ast_channel *tmp = NULL;
    
    	char group[BUFFERSIZE+1]="";
    
    	char buf[128];
    	char buf2[128], *ext=NULL, *port_str;
    	char *tokb=NULL, *p=NULL;
    	int channel=0, port=0;
    	struct misdn_bchannel *newbc = NULL;
    	
    	struct chan_list *cl=init_chan_list();
    	
    
    	sprintf(buf,"%s/%s",misdn_type,(char*)data);
    
    	ast_copy_string(buf2,data, 128);
    	
    
    	port_str=strtok_r(buf2,"/", &tokb);
    
    	ext=strtok_r(NULL,"/", &tokb);
    
    	if (port_str) {
    		if (port_str[0]=='g' && port_str[1]==':' ) {
    			/* We make a group call lets checkout which ports are in my group */
    			port_str += 2;
    			strncpy(group, port_str, BUFFERSIZE);
    			group[127] = 0;
    			chan_misdn_log(2, 0, " --> Group Call group: %s\n",group);
    		} 
    		else if ((p = strchr(port_str, ':'))) {
    			// we have a preselected channel
    			*p = 0;
    			channel = atoi(++p);
    			port = atoi(port_str);
    
    			chan_misdn_log(2, port, " --> Call on preselected Channel (%d).\n", channel);
    
    		}
    		else {
    			port = atoi(port_str);
    		}
    		
    		
    	} else {
    		ast_log(LOG_WARNING, " --> ! IND : CALL dad:%s WITHOUT PORT/Group, check extension.conf\n",ext);
    		return NULL;
    	}
    
    	if (!ast_strlen_zero(group)) {
    	
    
    		char cfg_group[BUFFERSIZE+1];
    
    		struct robin_list *rr = NULL;
    
    		if (misdn_cfg_is_group_method(group, METHOD_ROUND_ROBIN)) {
    			chan_misdn_log(4, port, " --> STARTING ROUND ROBIN...");
    			rr = get_robin_position(group);
    		}
    		
    		if (rr) {
    			int robin_channel = rr->channel;
    			int port_start;
    			int next_chan = 1;
    
    			do {
    				port_start = 0;
    				for (port = misdn_cfg_get_next_port_spin(rr->port); port > 0 && port != port_start;
    					 port = misdn_cfg_get_next_port_spin(port)) {
    
    					if (!port_start)
    						port_start = port;
    
    					if (port >= port_start)
    						next_chan = 1;
    					
    					if (port < port_start && next_chan) {
    						if (++robin_channel >= MAX_BCHANS) {
    							robin_channel = 1;
    						}
    						next_chan = 0;
    					}
    
    					misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, BUFFERSIZE);
    					
    					if (!strcasecmp(cfg_group, group)) {
    
    						int check;
    						misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(int));
    						port_up = misdn_lib_port_up(port, check);
    
    							newbc = misdn_lib_get_free_bc(port, robin_channel);
    							if (newbc) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    								chan_misdn_log(4, port, " Success! Found port:%d channel:%d\n", newbc->port, newbc->channel);
    
    									chan_misdn_log(4, port, "ortup:%d\n",  port_up);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    								rr->port = newbc->port;
    
    								rr->channel = newbc->channel;
    								break;
    							}
    						}
    					}
    				}
    			} while (!newbc && robin_channel != rr->channel);
    			
    			if (!newbc)
    				chan_misdn_log(4, port, " Failed! No free channel in group %d!", group);
    		}
    		
    		else {		
    			for (port=misdn_cfg_get_next_port(0); port > 0;
    				 port=misdn_cfg_get_next_port(port)) {
    				
    				misdn_cfg_get( port, MISDN_CFG_GROUPNAME, cfg_group, BUFFERSIZE);
    
    
    				chan_misdn_log(3,port, "Group [%s] Port [%d]\n", group, port);
    
    				if (!strcasecmp(cfg_group, group)) {
    
    					int check;
    					misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(int));
    					port_up = misdn_lib_port_up(port, check);
    
    					chan_misdn_log(4, port, "portup:%d\n", port_up);
    
    						newbc = misdn_lib_get_free_bc(port, 0);
    						if (newbc)
    							break;
    					}
    				}
    			}
    		}
    		
    	} else {
    		if (channel)
    			chan_misdn_log(1, port," --> preselected_channel: %d\n",channel);
    		newbc = misdn_lib_get_free_bc(port, channel);
    	}
    	
    	if (!newbc) {
    
    		chan_misdn_log(-1, 0, " --> ! No free channel chan ext:%s even after Group Call\n",ext);
    		chan_misdn_log(-1, 0, " --> SEND: State Down\n");
    
    
    	/* create ast_channel and link all the objects together */
    
    	tmp = misdn_new(cl, AST_STATE_RESERVED, ext, NULL, format, port, channel);
    	cl->ast=tmp;
    	
    	/* register chan in local list */
    	cl_queue_chan(&cl_te, cl) ;
    	
    	/* fill in the config into the objects */
    	read_config(cl, ORG_AST);
    	
    
    int misdn_send_text (struct ast_channel *chan, const char *text)
    {
    	struct chan_list *tmp=chan->tech_pvt;
    	
    	if (tmp && tmp->bc) {
    		ast_copy_string(tmp->bc->display,text,sizeof(tmp->bc->display));
    		misdn_lib_send_event(tmp->bc, EVENT_INFORMATION);
    	} else {
    		ast_log(LOG_WARNING, "No chan_list but send_text request?\n");
    		return -1;
    	}
    	
    	return 0;
    }
    
    
    static struct ast_channel_tech misdn_tech = {
    
    	.type="mISDN",
    	.description="Channel driver for mISDN Support (Bri/Pri)",
    	.capabilities= AST_FORMAT_ALAW ,
    	.requester=misdn_request,
    	.send_digit=misdn_digit,
    	.call=misdn_call,
    	.bridge=misdn_bridge, 
    	.hangup=misdn_hangup,
    	.answer=misdn_answer,
    	.read=misdn_read,
    	.write=misdn_write,
    	.indicate=misdn_indication,
    	.fixup=misdn_fixup,
    
    	.send_text=misdn_send_text,
    
    static struct ast_channel_tech misdn_tech_wo_bridge = {
    
    	.type="mISDN",
    	.description="Channel driver for mISDN Support (Bri/Pri)",
    	.capabilities=AST_FORMAT_ALAW ,
    	.requester=misdn_request,
    	.send_digit=misdn_digit,
    	.call=misdn_call,
    	.hangup=misdn_hangup,
    	.answer=misdn_answer,
    	.read=misdn_read,
    	.write=misdn_write,
    	.indicate=misdn_indication,
    	.fixup=misdn_fixup,
    
    	.send_text=misdn_send_text,
    
    static struct ast_channel *misdn_new(struct chan_list *chlist, int state,  char *exten, char *callerid, int format, int port, int c)
    
    	tmp = ast_channel_alloc(1);
    
    		chan_misdn_log(2, 0, " --> * NEW CHANNEL dad:%s oad:%s\n",exten,callerid);
    
    			ast_string_field_build(tmp, name, "%s/%d-u%d",
    
    			ast_string_field_build(tmp, name, "%s/%d-%d",
    
    		}
    		
    		tmp->nativeformats = prefformat;
    
    		tmp->readformat = format;
    		tmp->writeformat = format;
        
    		tmp->tech_pvt = chlist;
    
    		int bridging;
    		misdn_cfg_get( 0, MISDN_GEN_BRIDGING, &bridging, sizeof(int));
    		if (bridging)
    			tmp->tech = &misdn_tech;
    		else
    			tmp->tech = &misdn_tech_wo_bridge;
    
    		tmp->writeformat = format;
    		tmp->readformat = format;
    		tmp->priority=1;
    
    			ast_copy_string(tmp->exten, exten,  sizeof(tmp->exten));
    
    		else
    			chan_misdn_log(1,0,"misdn_new: no exten given.\n");
    
    		if (callerid) {
    			char *cid_name, *cid_num;
          
    			ast_callerid_parse(callerid, &cid_name, &cid_num);
    			if (cid_name)
    				tmp->cid.cid_name=strdup(cid_name);
    			if (cid_num)
    				tmp->cid.cid_num=strdup(cid_num);
    		}
    
    		{
    			if (pipe(chlist->pipe)<0)
    				perror("Pipe failed\n");
    			
    			tmp->fds[0]=chlist->pipe[0];
    			
    		}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		
    
    		ast_setstate(tmp, state);
    		if (state == AST_STATE_RING)
    			tmp->rings = 1;
    		else
    			tmp->rings = 0;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	} else {
    
    		chan_misdn_log(-1,0,"Unable to allocate channel structure\n");
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	}
    
    static int misdn_tx2ast_frm(struct chan_list * tmp, char * buf,  int len )
    
    	/* If in hold state we drop frame .. */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (tmp->holded ) return 0;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	switch(tmp->state) {
    	case MISDN_CLEANING:
    	case MISDN_EXTCANTMATCH:
    		return 0;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	default:
    		break;
    	}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		chan_misdn_log(3, tmp->bc->port, "misdn_tx2ast_frm: Returning because norxtone\n");
    
    		return 0;
    	}
    	
    	frame.frametype  = AST_FRAME_VOICE;
    	frame.subclass = AST_FORMAT_ALAW;
    	frame.datalen = len;
    	frame.samples = len ;
    	frame.mallocd =0 ;
    	frame.offset= 0 ;
    	frame.src = NULL;
    	frame.data = buf ;
    	
    	if (tmp->faxdetect || tmp->ast_dsp ) {
    		struct ast_frame *f,*f2;
    		if (tmp->trans)
    			f2=ast_translate(tmp->trans, &frame,0);
    		else {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			chan_misdn_log(0, tmp->bc->port, "No T-Path found\n");
    
    			return 0;
    		}
    		
    		f = ast_dsp_process(tmp->ast, tmp->dsp, f2);
    		if (f && (f->frametype == AST_FRAME_DTMF)) {
    			ast_log(LOG_DEBUG, "Detected inband DTMF digit: %c", f->subclass);
    			if (f->subclass == 'f' && tmp->faxdetect) {
    				/* Fax tone -- Handle and return NULL */
    				struct ast_channel *ast = tmp->ast;
    				if (!tmp->faxhandled) {
    					tmp->faxhandled++;
    					if (strcmp(ast->exten, "fax")) {
    
    						if (ast_exists_extension(ast, S_OR(ast->macrocontext, ast->context), "fax", 1, AST_CID_P(ast))) {
    
    							if (option_verbose > 2)
    								ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name);
    							/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
    							pbx_builtin_setvar_helper(ast,"FAXEXTEN",ast->exten);
    							if (ast_async_goto(ast, ast->context, "fax", 1))
    								ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, ast->context);
    						} else
    							ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n",ast->context, ast->exten);
    					} else
    						ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
    				} else
    					ast_log(LOG_DEBUG, "Fax already handled\n");
    				frame.frametype = AST_FRAME_NULL;
    				frame.subclass = 0;
    				f = &frame;
    			}  else if ( tmp->ast_dsp) {
    				struct ast_frame fr;
    				memset(&fr, 0 , sizeof(fr));
    				fr.frametype = AST_FRAME_DTMF;
    				fr.subclass = f->subclass ;
    				fr.src=NULL;
    				fr.data = NULL ;
    				fr.datalen = 0;
    				fr.samples = 0 ;
    				fr.mallocd =0 ;
    				fr.offset= 0 ;
    				
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n",f->subclass);
    
    				ast_queue_frame(tmp->ast, &fr);
    				
    				frame.frametype = AST_FRAME_NULL;
    				frame.subclass = 0;
    				f = &frame;
    			}
    		}
    	}
    	
    	if (tmp && tmp->ast && MISDN_ASTERISK_PVT (tmp->ast) && MISDN_ASTERISK_TECH_PVT(tmp->ast) ) {
    #if MISDN_DEBUG
    		int i, max=5>len?len:5;
        
    		printf("write2* %p %d bytes: ",tmp, len);
        
    		for (i=0; i<  max ; i++) printf("%2.2x ",((char*) frame.data)[i]);
    		printf ("\n");
    #endif
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		chan_misdn_log(9, tmp->bc->port, "Queueing %d bytes 2 Asterisk\n",len);
    
    		ast_queue_frame(tmp->ast,&frame);
    
    	}  else {
    		ast_log (LOG_WARNING, "No ast || ast->pvt || ch\n");
    	}
    	
    	return 0;
    }
    
    /** Channel Queue ***/
    
    
    static struct chan_list *find_chan_by_l3id(struct chan_list *list, unsigned long l3id)
    
    {
    	struct chan_list *help=list;
    	for (;help; help=help->next) {
    		if (help->l3id == l3id ) return help;
    	}
      
    
    	chan_misdn_log(6, list? (list->bc? list->bc->port : 0) : 0, "$$$ find_chan: No channel found with l3id:%x\n",l3id);
    
    static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc)
    
    {
    	struct chan_list *help=list;
    	for (;help; help=help->next) {
    		if (help->bc == bc) return help;
    	}
      
    
    	chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n",bc->oad,bc->dad);
    
    static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchannel *bc)
    
    	chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n",bc->channel, bc->oad,bc->dad);
    
    	for (;help; help=help->next) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n",help->bc->holded, help->bc->channel);
    		if (help->bc->port == bc->port
    
    		    && help->bc->holded ) return help;
    	}
    	
    
    	chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n",bc->oad,bc->dad);
    
    static void cl_queue_chan(struct chan_list **list, struct chan_list *chan)
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	chan_misdn_log(4, chan->bc? chan->bc->port : 0, "* Queuing chan %p\n",chan);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	ast_mutex_lock(&cl_te_lock);
    
    	if (!*list) {
    		*list = chan;
    	} else {
    		struct chan_list *help=*list;
    		for (;help->next; help=help->next); 
    		help->next=chan;
    	}
    	chan->next=NULL;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	ast_mutex_unlock(&cl_te_lock);
    
    static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) 
    
    {
    	if (chan->dsp) 
    		ast_dsp_free(chan->dsp);
    	if (chan->trans)
    		ast_translator_free_path(chan->trans);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	ast_mutex_lock(&cl_te_lock);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		ast_mutex_unlock(&cl_te_lock);
    
    		return;
    	}
      
    	if (*list == chan) {
    		*list=(*list)->next;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		ast_mutex_unlock(&cl_te_lock);
    
    		return ;
    	}
      
    	{
    		struct chan_list *help=*list;
    		for (;help->next; help=help->next) {
    			if (help->next == chan) {
    				help->next=help->next->next;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				ast_mutex_unlock(&cl_te_lock);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	ast_mutex_unlock(&cl_te_lock);
    
    }
    
    /** Channel Queue End **/
    
    
    
    /** Isdn asks us to release channel, pendant to misdn_hangup **/
    static void release_chan(struct misdn_bchannel *bc) {
    	struct ast_channel *ast=NULL;
    	
    	{
    		struct chan_list *ch=find_chan_by_bc(cl_te, bc);
    		if (!ch) ch=find_chan_by_l3id (cl_te, bc->l3_id);
    
    		if (!ch)  {
    			chan_misdn_log(0, bc->port, "release_chan: Ch not found!\n");
    			return;
    		}
    
    		
    		release_lock;
    		if (ch->ast) {
    			ast=ch->ast;
    		} 
    		release_unlock;
    		
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		chan_misdn_log(1, bc->port, "Trying to Release bc with l3id: %x\n",bc->l3_id);
    
    
    		//releaseing jitterbuffer
    		if (ch->jb ) {
    			misdn_jb_destroy(ch->jb);
    			ch->jb=NULL;
    		} else {
    			if (!bc->nojitter)
    				chan_misdn_log(5,bc->port,"Jitterbuffer already destroyed.\n");
    		}
    		
    
    		if (ch) {
    			
    			close(ch->pipe[0]);
    			close(ch->pipe[1]);
    			
    			if (ast && MISDN_ASTERISK_PVT(ast)) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n",bc?bc->pid:-1, ast->context, ast->exten,AST_CID_P(ast),misdn_get_ch_state(ch));
    				chan_misdn_log(3, bc->port, " --> * State Down\n");
    
    				/* copy cause */
    				send_cause2ast(ast,bc);
    				
    				MISDN_ASTERISK_TECH_PVT(ast)=NULL;
    				
          
    				if (ast->_state != AST_STATE_RESERVED) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(3, bc->port, " --> Setting AST State to down\n");
    
    					ast_setstate(ast, AST_STATE_DOWN);
    				}
    				
    				switch(ch->state) {
    				case MISDN_EXTCANTMATCH:
    				case MISDN_WAITING4DIGS:
    				{
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(3,  bc->port, " --> * State Wait4dig | ExtCantMatch\n");
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				case MISDN_CALLING_ACKNOWLEDGE:
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(2,  bc->port, "* --> In State Dialin\n");
    					chan_misdn_log(2,  bc->port, "* --> Queue Hangup\n");
    					
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					ast_queue_hangup(ast);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				case MISDN_CALLING:
    					
    					chan_misdn_log(2,  bc->port, "* --> In State Callin\n");
    					
    					if (!bc->nt) {
    						chan_misdn_log(2,  bc->port, "* --> Queue Hangup\n");
    						ast_queue_hangup(ast);
    					} else {
    						chan_misdn_log(2,  bc->port, "* --> Hangup\n");
    						ast_queue_hangup(ast);
    					}
    					break;
    					
    
    				case MISDN_CLEANING:
    					/* this state comes out of ast so we mustnt call a ast function ! */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(2,  bc->port, "* --> In StateCleaning\n");
    
    					break;
    				case MISDN_HOLD_DISCONNECT:
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(2,  bc->port, "* --> In HOLD_DISC\n");
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					chan_misdn_log(2,  bc->port, "* --> In State Default\n");
    					chan_misdn_log(2,  bc->port, "* --> Queue Hangup\n");
    
    						ast_queue_hangup(ast);
    					} else {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    						chan_misdn_log (0,  bc->port, "!! Not really queued!\n");
    
    					}
    				}
    			}
    			cl_dequeue_chan(&cl_te, ch);
    			
    			free(ch);
    		} else {
    			/* chan is already cleaned, so exiting  */
    		}
    	}
    }
    /*** release end **/
    
    
    static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan)
    
    {
    	chan_misdn_log(4,0,"TRANSFERING %s to %s\n",holded_chan->ast->name, tmp_ch->ast->name);
    	
    	tmp_ch->state=MISDN_HOLD_DISCONNECT;
      
    	ast_moh_stop(AST_BRIDGED_P(holded_chan->ast));
    
    	holded_chan->state=MISDN_CONNECTED;
    	holded_chan->holded=0;
    	misdn_lib_transfer(holded_chan->bc?holded_chan->bc:holded_chan->holded_bc);
    	ast_channel_masquerade(holded_chan->ast, AST_BRIDGED_P(tmp_ch->ast));
    }
    
    
    
    static void do_immediate_setup(struct misdn_bchannel *bc,struct chan_list *ch , struct ast_channel *ast)
    
    {
    	char predial[256]="";
    	char *p = predial;
      
    	struct ast_frame fr;
      
    	strncpy(predial, ast->exten, sizeof(predial) -1 );
      
    	ch->state=MISDN_DIALING;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (bc->nt) {
    
    		int ret; 
    		ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
    	} else {
    		int ret;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		if ( misdn_lib_is_ptp(bc->port)) {
    
    			ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE );
    		} else {
    			ret = misdn_lib_send_event(bc, EVENT_PROCEEDING );
    		}
    	}
    
    
    	if ( !bc->nt && (ch->orginator==ORG_MISDN) && !ch->incoming_early_audio ) 
    		chan_misdn_log(1,bc->port, " --> incoming_early_audio off\n");
    	 else