Skip to content
Snippets Groups Projects
channel.c 60.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->generator)
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_mutex_unlock(&chan->lock);
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->deferdtmf && strlen(chan->dtmfq)) {
    		/* We have DTMF that has been deferred.  Return it now */
    		chan->dtmff.frametype = AST_FRAME_DTMF;
    		chan->dtmff.subclass = chan->dtmfq[0];
    		/* Drop first digit */
    		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
    		pthread_mutex_unlock(&chan->lock);
    		return &chan->dtmff;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Read and ignore anything on the alertpipe, but read only
    	   one sizeof(blah) per frame that we send from it */
    	if (chan->pvt->alertpipe[0] > -1) {
    		read(chan->pvt->alertpipe[0], &blah, sizeof(blah));
    	}
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    	if ((chan->timingfd > -1) && (chan->fdno == AST_MAX_FDS - 2) && chan->exception) {
    		chan->exception = 0;
    		blah = -1;
    		ioctl(chan->timingfd, ZT_TIMERACK, &blah);
    		blah = 0;
    		ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
    		f =  &null_frame;
    		pthread_mutex_unlock(&chan->lock);
    		return f;
    	}
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for pending read queue */
    	if (chan->pvt->readq) {
    		f = chan->pvt->readq;
    		chan->pvt->readq = f->next;
    		/* Interpret hangup and return NULL */
    		if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))
    			f = NULL;
    	} else {
    		chan->blocker = pthread_self();
    		if (chan->exception) {
    			if (chan->pvt->exception) 
    				f = chan->pvt->exception(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else {
    				ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
    				f = &null_frame;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Clear the exception flag */
    			chan->exception = 0;
    		} else
    		if (chan->pvt->read)
    			f = chan->pvt->read(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (f && (f->frametype == AST_FRAME_VOICE)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!(f->subclass & chan->nativeformats)) {
    			/* This frame can't be from the current native formats -- drop it on the
    			   floor */
    
    			ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %d since our native format has changed to %d\n", chan->name, f->subclass, chan->nativeformats);
    			ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			f = &null_frame;
    
    		} else {
    			if (chan->monitor && chan->monitor->read_stream ) {
    				if( ast_writestream( chan->monitor->read_stream, f ) < 0 ) {
    					ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
    				}
    			}
    			if (chan->pvt->readtrans) {
    				f = ast_translate(chan->pvt->readtrans, f, 1);
    				if (!f)
    					f = &null_frame;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Make sure we always return NULL in the future */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!f) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    		if (chan->generator)
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* End the CDR if appropriate */
    		if (chan->cdr)
    			ast_cdr_end(chan->cdr);
    	} else if (chan->deferdtmf && f->frametype == AST_FRAME_DTMF) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
    			chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
    		else
    			ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
    		f = &null_frame;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
    		/* Answer the CDR */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(chan, AST_STATE_UP);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_mutex_unlock(&chan->lock);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Run any generator sitting on the line */
    	if (f && (f->frametype == AST_FRAME_VOICE) && chan->generatordata) {
    		/* Mask generator data temporarily */
    		void *tmp;
    		int res;
    		tmp = chan->generatordata;
    		chan->generatordata = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = chan->generator->generate(chan, tmp, f->datalen, f->samples);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = tmp;
    		if (res) {
    			ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
    			ast_deactivate_generator(chan);
    		}
    	}
    
    	if (chan->fin & 0x80000000)
    		ast_frame_dump(chan->name, f, "<<");
    	if ((chan->fin & 0x7fffffff) == 0x7fffffff)
    		chan->fin &= 0x80000000;
    	else
    		chan->fin++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return f;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_indicate(struct ast_channel *chan, int condition)
    {
    	int res = -1;
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->zombie || ast_check_hangup(chan)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->pvt->indicate)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = chan->pvt->indicate(chan, condition);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->pvt->indicate || res) {
    		/*
    		 * Device does not support (that) indication, lets fake
    		 * it by doing our own tone generation. (PM2002)
    		 */
    		if (condition >= 0) {
    			const struct tone_zone_sound *ts = NULL;
    			switch (condition) {
    			 case AST_CONTROL_RINGING:
    				ts = ast_get_indication_tone(chan->zone, "ring");
    				break;
    			 case AST_CONTROL_BUSY:
    				ts = ast_get_indication_tone(chan->zone, "busy");
    				break;
    			 case AST_CONTROL_CONGESTION:
    				ts = ast_get_indication_tone(chan->zone, "congestion");
    				break;
    			}
    			if (ts && ts->data[0]) {
    				ast_log(LOG_DEBUG, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
    
    				ast_playtones_start(chan,0,ts->data, 1);
    
    			} else if (condition == AST_CONTROL_PROGRESS) {
    				/* ast_playtones_stop(chan); */
    			} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* not handled */
    				ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
    				return -1;
    			}
    		}
    		else ast_playtones_stop(chan);
    	}
    	return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_recvchar(struct ast_channel *chan, int timeout)
    {
    	int res,ourto,c;
    	struct ast_frame *f;
    	
    	ourto = timeout;
    	for(;;)
    	   {
    		if (ast_check_hangup(chan)) return -1;
    		res = ast_waitfor(chan,ourto);
    		if (res <= 0) /* if timeout */
    		   {
    			return 0;
    		   }
    		ourto = res;
    		f = ast_read(chan);
    		if (f == NULL) return -1; /* if hangup */
    		if ((f->frametype == AST_FRAME_CONTROL) &&
    		    (f->subclass == AST_CONTROL_HANGUP)) return -1; /* if hangup */
    		if (f->frametype == AST_FRAME_TEXT)  /* if a text frame */
    		   {
    			c = *((char *)f->data);  /* get the data */
    			ast_frfree(f);
    			return(c);
    		   }
    		ast_frfree(f);
    	}
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_sendtext(struct ast_channel *chan, char *text)
    {
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->zombie || ast_check_hangup(chan)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    	if (chan->pvt->send_text)
    		res = chan->pvt->send_text(chan, text);
    	chan->blocking = 0;
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int do_senddigit(struct ast_channel *chan, char digit)
    {
    	int res = -1;
    
    	if (chan->pvt->send_digit)
    		res = chan->pvt->send_digit(chan, digit);
    	if (!chan->pvt->send_digit || res) {
    		/*
    		 * Device does not support DTMF tones, lets fake
    		 * it by doing our own generation. (PM2002)
    		 */
    		static const char* dtmf_tones[] = {
    			"!941+1336/50,!0/50",	/* 0 */
    			"!697+1209/50,!0/50",	/* 1 */
    			"!697+1336/50,!0/50",	/* 2 */
    			"!697+1477/50,!0/50",	/* 3 */
    			"!770+1209/50,!0/50",	/* 4 */
    			"!770+1336/50,!0/50",	/* 5 */
    			"!770+1477/50,!0/50",	/* 6 */
    			"!852+1209/50,!0/50",	/* 7 */
    			"!852+1336/50,!0/50",	/* 8 */
    			"!852+1477/50,!0/50",	/* 9 */
    			"!697+1633/50,!0/50",	/* A */
    			"!770+1633/50,!0/50",	/* B */
    			"!852+1633/50,!0/50",	/* C */
    			"!941+1633/50,!0/50",	/* D */
    			"!941+1209/50,!0/50",	/* * */
    			"!941+1477/50,!0/50" };	/* # */
    		if (digit >= '0' && digit <='9')
    
    			ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit >= 'A' && digit <= 'D')
    
    			ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '*')
    
    			ast_playtones_start(chan,0,dtmf_tones[14], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '#')
    
    			ast_playtones_start(chan,0,dtmf_tones[15], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else {
    			/* not handled */
    			ast_log(LOG_WARNING, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name);
    			return -1;
    		}
    	}
    	return 0;
    }
    
    
    int ast_prod(struct ast_channel *chan)
    {
    	struct ast_frame a = { AST_FRAME_VOICE };
    	char nothing[128];
    	/* Send an empty audio frame to get things moving */
    	if (chan->_state != AST_STATE_UP) {
    
    		ast_log(LOG_DEBUG, "Prodding channel '%s'\n", chan->name);
    
    		a.subclass = chan->pvt->rawwriteformat;
    		a.data = nothing + AST_FRIENDLY_OFFSET;
    
    		if (ast_write(chan, &a))
    			ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_write(struct ast_channel *chan, struct ast_frame *fr)
    {
    	int res = -1;
    
    	struct ast_frame *f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->zombie || ast_check_hangup(chan)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	/* Handle any pending masquerades */
    	if (chan->masq) {
    		if (ast_do_masquerade(chan)) {
    			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    			return -1;
    		}
    	}
    	if (chan->masqr)
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    		if (chan->writeinterrupt)
    			ast_deactivate_generator(chan);
    		else
    			return 0;
    	}
    
    	if (chan->fout & 0x80000000)
    		ast_frame_dump(chan->name, fr, ">>");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    	switch(fr->frametype) {
    	case AST_FRAME_CONTROL:
    		/* XXX Interpret control frames XXX */
    		ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
    		break;
    	case AST_FRAME_DTMF:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = do_senddigit(chan,fr->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FRAME_TEXT:
    		if (chan->pvt->send_text)
    			res = chan->pvt->send_text(chan, (char *) fr->data);
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->pvt->write) {
    			if (chan->pvt->writetrans) {
    
    				f = ast_translate(chan->pvt->writetrans, fr, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else
    				f = fr;
    			if (f)	
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = chan->pvt->write(chan, f);
    
    				if( chan->monitor &&
    						chan->monitor->write_stream &&
    						f && ( f->frametype == AST_FRAME_VOICE ) ) {
    					if( ast_writestream( chan->monitor->write_stream, f ) < 0 ) {
    						ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
    					}
    				}
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else
    				res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (f && (f != fr))
    		ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->blocking = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Consider a write failure to force a soft hangup */
    	if (res < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    	else {
    		if ((chan->fout & 0x7fffffff) == 0x7fffffff)
    			chan->fout &= 0x80000000;
    		else
    			chan->fout++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->fout++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_set_write_format(struct ast_channel *chan, int fmts)
    {
    	int fmt;
    	int native;
    	int res;
    	
    	native = chan->nativeformats;
    	fmt = fmts;
    	
    	res = ast_translator_best_choice(&native, &fmt);
    	if (res < 0) {
    		ast_log(LOG_NOTICE, "Unable to find a path from %d to %d\n", fmts, chan->nativeformats);
    		return -1;
    	}
    	
    	/* Now we have a good choice for both.  We'll write using our native format. */
    	chan->pvt->rawwriteformat = native;
    	/* User perspective is fmt */
    	chan->writeformat = fmt;
    	/* Free any write translation we have right now */
    	if (chan->pvt->writetrans)
    		ast_translator_free_path(chan->pvt->writetrans);
    	/* Build a translation path from the user write format to the raw writing format */
    	chan->pvt->writetrans = ast_translator_build_path(chan->pvt->rawwriteformat, chan->writeformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_DEBUG, "Set channel %s to write format %d\n", chan->name, chan->writeformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    int ast_set_read_format(struct ast_channel *chan, int fmts)
    {
    	int fmt;
    	int native;
    	int res;
    	
    	native = chan->nativeformats;
    	fmt = fmts;
    	/* Find a translation path from the native read format to one of the user's read formats */
    	res = ast_translator_best_choice(&fmt, &native);
    	if (res < 0) {
    		ast_log(LOG_NOTICE, "Unable to find a path from %d to %d\n", chan->nativeformats, fmts);
    		return -1;
    	}
    	
    	/* Now we have a good choice for both.  We'll write using our native format. */
    	chan->pvt->rawreadformat = native;
    	/* User perspective is fmt */
    	chan->readformat = fmt;
    	/* Free any read translation we have right now */
    	if (chan->pvt->readtrans)
    		ast_translator_free_path(chan->pvt->readtrans);
    	/* Build a translation path from the raw read format to the user reading format */
    	chan->pvt->readtrans = ast_translator_build_path(chan->readformat, chan->pvt->rawreadformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Set channel %s to read format %d\n", chan->name, chan->readformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate, char *callerid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int state = 0;
    	struct ast_channel *chan;
    	struct ast_frame *f;
    	int res;
    	
    	chan = ast_request(type, format, data);
    	if (chan) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (callerid)
    			ast_set_callerid(chan, callerid, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!ast_call(chan, data, 0)) {
    			while(timeout && (chan->_state != AST_STATE_UP)) {
    				res = ast_waitfor(chan, timeout);
    				if (res < 0) {
    					/* Something not cool, or timed out */
    					ast_hangup(chan);
    					chan = NULL;
    					break;
    				}
    				/* If done, break out */
    				if (!res)
    					break;
    				if (timeout > -1)
    					timeout = res;
    				f = ast_read(chan);
    				if (!f) {
    					state = AST_CONTROL_HANGUP;
    					ast_hangup(chan);
    					chan = NULL;
    					break;
    				}
    				if (f->frametype == AST_FRAME_CONTROL) {
    					if (f->subclass == AST_CONTROL_RINGING)
    						state = AST_CONTROL_RINGING;
    					else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
    						state = f->subclass;
    						break;
    					} else if (f->subclass == AST_CONTROL_ANSWER) {
    						state = f->subclass;
    						break;
    					} else {
    						ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
    					}
    				}
    				ast_frfree(f);
    			}
    		} else {
    			ast_hangup(chan);
    			chan = NULL;
    			ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    		}
    	} else
    		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    	if (chan && (chan->_state == AST_STATE_UP))
    		state = AST_CONTROL_ANSWER;
    	if (outstate)
    		*outstate = state;
    	return chan;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_channel *ast_request(char *type, int format, void *data)
    {
    	struct chanlist *chan;
    	struct ast_channel *c = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int capabilities;
    	int fmt;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (PTHREAD_MUTEX_LOCK(&chlock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return NULL;
    	}
    	chan = backends;
    	while(chan) {
    		if (!strcasecmp(type, chan->type)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			capabilities = chan->capabilities;
    			fmt = format;
    			res = ast_translator_best_choice(&fmt, &capabilities);
    			if (res < 0) {
    				ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->capabilities, format);
    				PTHREAD_MUTEX_UNLOCK(&chlock);
    				return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (chan->requester)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				c = chan->requester(type, capabilities, data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (c) {
    
    //				ast_device_state_changed(c->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				manager_event(EVENT_FLAG_CALL, "Newchannel",
    				"Channel: %s\r\n"
    				"State: %s\r\n"
    				"Callerid: %s\r\n",
    				c->name, ast_state2str(c->_state), c->callerid ? c->callerid : "<unknown>");
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		chan = chan->next;
    	}
    	if (!chan)
    		ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return c;
    }
    
    
    int ast_parse_device_state(char *device)
    {
    	char name[AST_CHANNEL_NAME] = "";
    	char *cut;
    	struct ast_channel *chan;
    
    	chan = ast_channel_walk(NULL);
    	while (chan) {
    		strncpy(name, chan->name, sizeof(name)-1);
    		cut = strchr(name,'-');
    		if (cut)
    		        *cut = 0;
    		if (!strcmp(name, device))
    		        return AST_DEVICE_INUSE;
    		chan = ast_channel_walk(chan);
    	}
    	return AST_DEVICE_UNKNOWN;
    }
    
    int ast_device_state(char *device)
    {
    	char tech[AST_MAX_EXTENSION] = "";
    	char *number;
    	struct chanlist *chanls;
    	int res = 0;
    	
    	strncpy(tech, device, sizeof(tech)-1);
    	number = strchr(tech, '/');
    	if (!number) {
    	    return AST_DEVICE_INVALID;
    	}
    	*number = 0;
    	number++;
    		
    	if (PTHREAD_MUTEX_LOCK(&chlock)) {
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return -1;
    	}
    	chanls = backends;
    	while(chanls) {
    		if (!strcasecmp(tech, chanls->type)) {
    			PTHREAD_MUTEX_UNLOCK(&chlock);
    			if (!chanls->devicestate) 
    				return ast_parse_device_state(device);
    			else {
    				res = chanls->devicestate(number);
    				if (res == AST_DEVICE_UNKNOWN)
    					return ast_parse_device_state(device);
    				else
    					return res;
    			}
    		}
    		chanls = chanls->next;
    	}
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    	return AST_DEVICE_INVALID;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_call(struct ast_channel *chan, char *addr, int timeout) 
    {
    	/* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
    	   If the remote end does not answer within the timeout, then do NOT hang up, but 
    	   return anyway.  */
    	int res = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->zombie && !ast_check_hangup(chan)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->pvt->call)
    			res = chan->pvt->call(chan, addr, timeout);
    	pthread_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_transfer(struct ast_channel *chan, char *dest) 
    {
    	/* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
    	   If the remote end does not answer within the timeout, then do NOT hang up, but 
    	   return anyway.  */
    	int res = -1;
    	/* Stop if we're a zombie or need a soft hangup */
    	ast_pthread_mutex_lock(&chan->lock);
    	if (!chan->zombie && !ast_check_hangup(chan)) {
    		if (chan->pvt->transfer) {
    			res = chan->pvt->transfer(chan, dest);
    			if (!res)
    				res = 1;
    		} else
    			res = 0;
    	}
    	pthread_mutex_unlock(&chan->lock);
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
    {
    	int pos=0;
    	int to = ftimeout;
    	char d;
    
    	/* XXX Merge with full version? XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (c->zombie || ast_check_hangup(c)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!len)
    		return -1;
    	do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->streamid > -1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			d = ast_waitstream(c, AST_DIGIT_ANY);
    			ast_stopstream(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			usleep(1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!d)
    				d = ast_waitfordigit(c, to);
    		} else {
    			d = ast_waitfordigit(c, to);
    		}
    		if (d < 0)
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strchr(enders, d))
    			s[pos++] = d;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strchr(enders, d) || (pos >= len)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			s[pos]='\0';
    
    			return 0;
    		}
    		to = timeout;
    	} while(1);
    	/* Never reached */
    	return 0;
    }
    
    int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
    {
    	int pos=0;
    	int to = ftimeout;
    	char d;
    	/* Stop if we're a zombie or need a soft hangup */
    	if (c->zombie || ast_check_hangup(c)) 
    		return -1;
    	if (!len)
    		return -1;
    	do {
    		if (c->streamid > -1) {
    			d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
    			ast_stopstream(c);
    			usleep(1000);
    			if (!d)
    				d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		} else {
    			d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		}
    		if (d < 0)
    			return -1;
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    		if (d == 1) {
    			s[pos]='\0';
    			return 2;
    		}
    		if (!strchr(enders, d))
    			s[pos++] = d;
    		if (strchr(enders, d) || (pos >= len)) {
    			s[pos]='\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		to = timeout;
    	} while(1);
    	/* Never reached */
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_supports_html(struct ast_channel *chan)
    {
    	if (chan->pvt->send_html)
    		return 1;
    	return 0;
    }
    
    int ast_channel_sendhtml(struct ast_channel *chan, int subclass, char *data, int datalen)
    {
    	if (chan->pvt->send_html)
    		return chan->pvt->send_html(chan, subclass, data, datalen);
    	return -1;
    }
    
    int ast_channel_sendurl(struct ast_channel *chan, char *url)
    {
    	if (chan->pvt->send_html)
    		return chan->pvt->send_html(chan, AST_HTML_URL, url, strlen(url) + 1);
    	return -1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
    {
    	int peerf;
    	int chanf;
    	int res;
    	peerf = peer->nativeformats;
    	chanf = chan->nativeformats;
    	res = ast_translator_best_choice(&peerf, &chanf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, chan->nativeformats, peer->name, peer->nativeformats);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	/* Set read format on channel */
    	res = ast_set_read_format(chan, peerf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, chanf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	/* Set write format on peer channel */
    	res = ast_set_write_format(peer, peerf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, peerf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	/* Now we go the other way */
    	peerf = peer->nativeformats;
    	chanf = chan->nativeformats;
    	res = ast_translator_best_choice(&chanf, &peerf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, peer->nativeformats, chan->name, chan->nativeformats);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	/* Set writeformat on channel */
    	res = ast_set_write_format(chan, chanf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, chanf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	/* Set read format on peer channel */
    	res = ast_set_read_format(peer, chanf);
    	if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, peerf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
    {
    
    	struct ast_frame null = { AST_FRAME_NULL, };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_log(LOG_DEBUG, "Planning to masquerade %s into the structure of %s\n",
    		clone->name, original->name);
    	if (original->masq) {
    		ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", 
    			original->masq->name, original->name);
    		return -1;
    	}
    	if (clone->masqr) {
    		ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", 
    			clone->name, clone->masqr->name);
    		return -1;
    	}
    	original->masq = clone;
    	clone->masqr = original;
    
    	/* XXX can't really hold the lock here, but at the same time, it' s
    	   not really safe not to XXX */
    	ast_queue_frame(original, &null, 0);
    
    	ast_queue_frame(clone, &null, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_change_name(struct ast_channel *chan, char *newname)
    {
    	char tmp[256];
    	strncpy(tmp, chan->name, 256);
    	strncpy(chan->name, newname, sizeof(chan->name) - 1);
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", tmp, chan->name);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int ast_do_masquerade(struct ast_channel *original)
    {
    	int x;
    	int res=0;
    	char *tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	void *tmpv;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel_pvt *p;
    	struct ast_channel *clone = original->masq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int rformat = original->readformat;
    	int wformat = original->writeformat;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char newn[100];
    	char orig[100];
    	char masqn[100];
    	char zombn[100];
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 1
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    		clone->name, clone->_state, original->name, original->_state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* XXX This is a seriously wacked out operation.  We're essentially putting the guts of
    	   the clone channel into the original channel.  Start by killing off the original
    	   channel's backend.   I'm not sure we're going to keep this function, because 
    	   while the features are nice, the cost is very high in terms of pure nastiness. XXX */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Having remembered the original read/write formats, we turn off any translation on either
    	   one */
    	free_translation(clone);
    	free_translation(original);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* We need the clone's lock, too */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&clone->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Unlink the masquerade */
    	original->masq = NULL;
    	clone->masqr = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* Save the original name */
    	strncpy(orig, original->name, sizeof(orig) - 1);
    	/* Save the new name */
    	strncpy(newn, clone->name, sizeof(newn) - 1);
    	/* Create the masq name */
    	snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		
    	/* Copy the name from the clone channel */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	strncpy(original->name, newn, sizeof(original->name)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Mangle the name of the clone channel */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	strncpy(clone->name, masqn, sizeof(clone->name) - 1);
    	
    	/* Notify any managers of the change, first the masq then the other */
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", newn, masqn);
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", orig, newn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Swap the guts */	
    	p = original->pvt;
    	original->pvt = clone->pvt;
    	clone->pvt = p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	clone->_softhangup = AST_SOFTHANGUP_DEV;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    	if (clone->pvt->fixup){
    		res = clone->pvt->fixup(original, clone);
    		if (res) 
    			ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Start by disconnecting the original's physical side */
    	if (clone->pvt->hangup)
    		res = clone->pvt->hangup(clone);
    	if (res) {
    		ast_log(LOG_WARNING, "Hangup failed!  Strange things may happen!\n");
    		pthread_mutex_unlock(&clone->lock);
    		return -1;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Mangle the name of the clone channel */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	strncpy(clone->name, zombn, sizeof(clone->name) - 1);
    	manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", masqn, zombn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Keep the same language.  */
    	/* Update the type. */
    	original->type = clone->type;
    	/* Copy the FD's */
    	for (x=0;x<AST_MAX_FDS;x++)
    		original->fds[x] = clone->fds[x];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Move the variables */
    	tmpv = original->varshead.first;
    	original->varshead.first = clone->varshead.first;
    	clone->varshead.first = tmpv;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Presense of ADSI capable CPE follows clone */
    	original->adsicpe = clone->adsicpe;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Bridge remains the same */
    	/* CDR fields remain the same */
    	/* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
    	/* Application and data remain the same */
    	/* Clone exception  becomes real one, as with fdno */
    	original->exception = clone->exception;
    	original->fdno = clone->fdno;
    	/* Schedule context remains the same */
    	/* Stream stuff stays the same */
    	/* Keep the original state.  The fixup code will need to work with it most likely */
    
    	/* dnid and callerid change to become the new, HOWEVER, we also link the original's
    	   fields back into the defunct 'clone' so that they will be freed when
    	   ast_frfree is eventually called */
    	tmp = original->dnid;
    	original->dnid = clone->dnid;
    	clone->dnid = tmp;
    	
    	tmp = original->callerid;
    	original->callerid = clone->callerid;
    	clone->callerid = tmp;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Our native formats are different now */
    	original->nativeformats = clone->nativeformats;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* And of course, so does our current state.  Note we need not
    	   call ast_setstate since the event manager doesn't really consider
    	   these separate */
    	original->_state = clone->_state;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Context, extension, priority, app data, jump table,  remain the same */
    	/* pvt switches.  pbx stays the same, as does next */
    	
    	/* Now, at this point, the "clone" channel is totally F'd up.  We mark it as
    	   a zombie so nothing tries to touch it.  If it's already been marked as a
    	   zombie, then free it now (since it already is considered invalid). */
    	if (clone->zombie) {
    		pthread_mutex_unlock(&clone->lock);
    		ast_channel_free(clone);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		manager_event(EVENT_FLAG_CALL, "Hangup", "Channel: %s\r\n", zombn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		clone->zombie=1;
    		pthread_mutex_unlock(&clone->lock);
    	}
    	/* Set the write format */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_set_write_format(original, wformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Set the read format */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_set_read_format(original, rformat);
    
    	ast_log(LOG_DEBUG, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Okay.  Last thing is to let the channel driver know about all this mess, so he
    	   can fix up everything as best as possible */
    	if (original->pvt->fixup) {
    		res = original->pvt->fixup(clone, original);
    		if (res) {
    			ast_log(LOG_WARNING, "Driver for '%s' could not fixup channel %s\n",
    				original->type, original->name);
    			return -1;
    		}
    	} else
    		ast_log(LOG_WARNING, "Driver '%s' does not have a fixup routine (for %s)!  Bad things may happen.\n",
    			original->type, original->name);
    	/* Signal any blocker */
    	if (original->blocking)
    		pthread_kill(original->blocker, SIGURG);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_set_callerid(struct ast_channel *chan, char *callerid, int anitoo)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	if (chan->callerid)
    		free(chan->callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (anitoo && chan->ani)
    		free(chan->ani);
    	if (callerid) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->callerid = strdup(callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (anitoo)
    			chan->ani = strdup(callerid);
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->callerid = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (anitoo)
    			chan->ani = NULL;
    	}
    	if (chan->cdr)
    		ast_cdr_setcid(chan->cdr, chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Newcallerid", 
    				"Channel: %s\r\n"
    				"Callerid: %s\r\n",
    				chan->name, chan->callerid ? 
    				chan->callerid : "<Unknown>");
    }
    
    int ast_setstate(struct ast_channel *chan, int state)
    {
    	if (chan->_state != state) {
    		int oldstate = chan->_state;
    		chan->_state = state;
    		if (oldstate == AST_STATE_DOWN) {
    
    			ast_device_state_changed(chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			manager_event(EVENT_FLAG_CALL, "Newchannel",
    			"Channel: %s\r\n"
    			"State: %s\r\n"
    			"Callerid: %s\r\n",
    			chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>");
    		} else {
    			manager_event(EVENT_FLAG_CALL, "Newstate", 
    				"Channel: %s\r\n"
    				"State: %s\r\n"
    				"Callerid: %s\r\n",
    				chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "<unknown>");
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
    {
    	/* Copy voice back and forth between the two channels.  Give the peer
    	   the ability to transfer calls with '#<extension' syntax. */
    	struct ast_channel *cs[3];
    	int to = -1;
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *who = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int nativefailed=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */