예제 #1
0
static int phone_hangup(struct ast_channel *ast)
{
	struct phone_pvt *p;
	p = ast->pvt->pvt;
	if (option_debug)
		ast_log(LOG_DEBUG, "phone_hangup(%s)\n", ast->name);
	if (!ast->pvt->pvt) {
		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
		return 0;
	}
	/* XXX Is there anything we can do to really hang up except stop recording? */
	ast_setstate(ast, AST_STATE_DOWN);
	if (ioctl(p->fd, PHONE_REC_STOP))
		ast_log(LOG_WARNING, "Failed to stop recording\n");
	if (ioctl(p->fd, PHONE_PLAY_STOP))
		ast_log(LOG_WARNING, "Failed to stop playing\n");
	if (ioctl(p->fd, PHONE_RING_STOP))
		ast_log(LOG_WARNING, "Failed to stop ringing\n");
	if (ioctl(p->fd, PHONE_CPT_STOP))
		ast_log(LOG_WARNING, "Failed to stop sounds\n");

	/* If it's an FXO, hang them up */
	if (p->mode == MODE_FXO) {
		if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) 
			ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno));
	}

	/* If they're off hook, give a busy signal */
	if (ioctl(p->fd, PHONE_HOOKSTATE)) {
		if (option_debug)
			ast_log(LOG_DEBUG, "Got hunghup, giving busy signal\n");
		ioctl(p->fd, PHONE_BUSY);
		p->cpt = 1;
	}
	p->lastformat = -1;
	p->lastinput = -1;
	p->ministate = 0;
	p->obuflen = 0;
	p->dialtone = 0;
	memset(p->ext, 0, sizeof(p->ext));
	((struct phone_pvt *)(ast->pvt->pvt))->owner = NULL;
	ast_mutex_lock(&usecnt_lock);
	usecnt--;
	if (usecnt < 0) 
		ast_log(LOG_WARNING, "Usecnt < 0???\n");
	ast_mutex_unlock(&usecnt_lock);
	ast_update_use_count();
	if (option_verbose > 2) 
		ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name);
	ast->pvt->pvt = NULL;
	ast_setstate(ast, AST_STATE_DOWN);
	restart_monitor();
	return 0;
}
예제 #2
0
static int status_session(struct respoke_session *session, enum respoke_status status)
{
	if (!session->channel) {
		return 0;
	}

	switch (status) {
	case RESPOKE_STATUS_RINGING:
		ast_queue_control(session->channel, AST_CONTROL_RINGING);
		ast_channel_lock(session->channel);
		if (ast_channel_state(session->channel) != AST_STATE_UP) {
			ast_setstate(session->channel, AST_STATE_RINGING);
		}
		ast_channel_unlock(session->channel);
		break;
	case RESPOKE_STATUS_PROGRESS:
		ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
		break;
	default:
		set_cause_code(session, status);
		ast_queue_hangup(session->channel);
		break;
	}

	return 0;
}
static struct ast_frame *aopen_handle_escape(struct ast_modem_pvt *p, char esc)
{
	/* Handle escaped characters -- but sometimes we call it directly as 
	   a quick way to cause known responses */
	p->fr.frametype = AST_FRAME_NULL;
	p->fr.subclass = 0;
	p->fr.data = NULL;
	p->fr.datalen = 0;
	p->fr.samples = 0;
	p->fr.offset = 0;
	p->fr.mallocd = 0;
        p->fr.delivery.tv_sec = 0;
        p->fr.delivery.tv_usec = 0;
	if (esc)
		ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
	
	switch(esc) {
	case 'R': /* Pseudo ring */
		p->fr.frametype = AST_FRAME_CONTROL;
		p->fr.subclass = AST_CONTROL_RING;
		return &p->fr;
	case 'X': /* Pseudo connect */
		p->fr.frametype = AST_FRAME_CONTROL;
		p->fr.subclass = AST_CONTROL_RING;
		if (p->owner)
			ast_setstate(p->owner, AST_STATE_UP);
		if (aopen_startrec(p))
			return 	NULL;
		return &p->fr;
	case 'b': /* Busy signal */
		p->fr.frametype = AST_FRAME_CONTROL;
		p->fr.subclass = AST_CONTROL_BUSY;
		return &p->fr;
	case 'o': /* Overrun */
		ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
		if (ast_modem_send(p, "\0x10E", 2)) 
			ast_log(LOG_WARNING, "Unable to flush buffers\n");
		return &p->fr;	
	case 'u': /* Underrun */
		ast_log(LOG_WARNING, "Data underrun\n");
		/* Fall Through */
	case CHAR_ETX: /* End Transmission */
	case 'd': /* Dialtone */
	case 'c': /* Calling Tone */
	case 'e': /* European version */
	case 'a': /* Answer Tone */
	case 'f': /* Bell Answer Tone */
	case 'T': /* Timing mark */
	case 't': /* Handset off hook */
	case 'h': /* Handset hungup */
	case 0: /* Pseudo signal */
		/* Ignore */
		return &p->fr;	
	default:
		ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
	}
	return &p->fr;
}
예제 #4
0
static int phone_call(struct ast_channel *ast, char *dest, int timeout)
{
	struct phone_pvt *p;

	PHONE_CID cid;
	time_t UtcTime;
	struct tm tm;

	time(&UtcTime);
	localtime_r(&UtcTime,&tm);

	memset(&cid, 0, sizeof(PHONE_CID));
	if(&tm != NULL) {
		snprintf(cid.month, sizeof(cid.month), "%02d",(tm.tm_mon + 1));
		snprintf(cid.day, sizeof(cid.day),     "%02d", tm.tm_mday);
		snprintf(cid.hour, sizeof(cid.hour),   "%02d", tm.tm_hour);
		snprintf(cid.min, sizeof(cid.min),     "%02d", tm.tm_min);
	}
	/* the standard format of ast->callerid is:  "name" <number>, but not always complete */
	if (!ast->callerid || ast_strlen_zero(ast->callerid)){
		strncpy(cid.name, DEFAULT_CALLER_ID, sizeof(cid.name) - 1);
		cid.number[0]='\0';
	} else {
		char *n, *l;
		char callerid[256] = "";
		strncpy(callerid, ast->callerid, sizeof(callerid) - 1);
		ast_callerid_parse(callerid, &n, &l);
		if (l) {
			ast_shrink_phone_number(l);
			if (!ast_isphonenumber(l))
				l = NULL;
		}
		if (l)
			strncpy(cid.number, l, sizeof(cid.number) - 1);
		if (n)
			strncpy(cid.name, n, sizeof(cid.name) - 1);
	}

	p = ast->pvt->pvt;

	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
		ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name);
		return -1;
	}
	if (option_debug)
		ast_log(LOG_DEBUG, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]);

	IXJ_PHONE_RING_START(cid);
	ast_setstate(ast, AST_STATE_RINGING);
	ast_queue_control(ast, AST_CONTROL_RINGING);
	return 0;
}
예제 #5
0
static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context)
{
	struct ast_channel *tmp;
	tmp = ast_channel_alloc(1);
	if (tmp) {
		snprintf(tmp->name, sizeof(tmp->name), "Phone/%s", i->dev + 5);
		tmp->type = type;
		tmp->fds[0] = i->fd;
		/* XXX Switching formats silently causes kernel panics XXX */
		tmp->nativeformats = prefformat;
		tmp->pvt->rawreadformat = prefformat;
		tmp->pvt->rawwriteformat = prefformat;
		ast_setstate(tmp, state);
		if (state == AST_STATE_RING)
			tmp->rings = 1;
		tmp->pvt->pvt = i;
		tmp->pvt->send_digit = phone_digit;
		tmp->pvt->call = phone_call;
		tmp->pvt->hangup = phone_hangup;
		tmp->pvt->answer = phone_answer;
		tmp->pvt->read = phone_read;
		tmp->pvt->write = phone_write;
		tmp->pvt->exception = phone_exception;
		strncpy(tmp->context, context, sizeof(tmp->context)-1);
		if (strlen(i->ext))
			strncpy(tmp->exten, i->ext, sizeof(tmp->exten)-1);
		else
			strncpy(tmp->exten, "s",  sizeof(tmp->exten) - 1);
		if (strlen(i->language))
			strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
		if (strlen(i->callerid))
			tmp->callerid = strdup(i->callerid);
		i->owner = tmp;
		ast_mutex_lock(&usecnt_lock);
		usecnt++;
		ast_mutex_unlock(&usecnt_lock);
		ast_update_use_count();
		if (state != AST_STATE_DOWN) {
			if (state == AST_STATE_RING) {
				ioctl(tmp->fds[0], PHONE_RINGBACK);
				i->cpt = 1;
			}
			if (ast_pbx_start(tmp)) {
				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
				ast_hangup(tmp);
			}
		}
	} else
		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
	return tmp;
}
예제 #6
0
static inline int at_response_conn (pvt_t* pvt)
{
	if (pvt->outgoing)
	{
		ast_debug (1, "[%s] Remote end answered\n", pvt->id);
		channel_queue_control (pvt, AST_CONTROL_ANSWER);
	}
	else if (pvt->incoming && pvt->answered)
	{
		ast_setstate (pvt->owner, AST_STATE_UP);
	}

	return 0;
}
예제 #7
0
/*!
 * \ingroup applications
 */
int indicate_congestion(struct ast_channel *chan, const char *data)
{
	ast_indicate(chan, AST_CONTROL_CONGESTION);
	/* Don't change state of an UP channel, just indicate
	   congestion in audio */
	ast_channel_lock(chan);
	if (ast_channel_state(chan) != AST_STATE_UP) {
		ast_channel_hangupcause_set(chan, AST_CAUSE_CONGESTION);
		ast_setstate(chan, AST_STATE_BUSY);
	}
	ast_channel_unlock(chan);
	wait_for_hangup(chan, data);
	return -1;
}
예제 #8
0
static int phone_answer(struct ast_channel *ast)
{
	struct phone_pvt *p;
	p = ast->pvt->pvt;
	/* In case it's a LineJack, take it off hook */
	if (p->mode == MODE_FXO) {
		if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK)) 
			ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno));
		else
			ast_log(LOG_DEBUG, "Took linejack off hook\n");
	}
	phone_setup(ast);
	if (option_debug)
		ast_log(LOG_DEBUG, "phone_answer(%s)\n", ast->name);
	ast->rings = 0;
	ast_setstate(ast, AST_STATE_UP);
	return 0;
}
예제 #9
0
/*!
 * \brief queue a frame onto either the p->owner or p->chan
 *
 * \note the ast_unreal_pvt MUST have it's ref count bumped before entering this function and
 * decremented after this function is called.  This is a side effect of the deadlock
 * avoidance that is necessary to lock 2 channels and a tech_pvt.  Without a ref counted
 * ast_unreal_pvt, it is impossible to guarantee it will not be destroyed by another thread
 * during deadlock avoidance.
 */
static int unreal_queue_frame(struct ast_unreal_pvt *p, int isoutbound, struct ast_frame *f,
	struct ast_channel *us, int us_locked)
{
	struct ast_channel *other;

	/* Recalculate outbound channel */
	other = isoutbound ? p->owner : p->chan;
	if (!other) {
		return 0;
	}

	/* do not queue media frames if a generator is on both unreal channels */
	if (us
		&& (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)
		&& ast_channel_generator(us)
		&& ast_channel_generator(other)) {
		return 0;
	}

	/* grab a ref on the channel before unlocking the pvt,
	 * other can not go away from us now regardless of locking */
	ast_channel_ref(other);
	if (us && us_locked) {
		ast_channel_unlock(us);
	}
	ao2_unlock(p);

	if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) {
		ast_setstate(other, AST_STATE_RINGING);
	}
	ast_queue_frame(other, f);

	other = ast_channel_unref(other);
	if (us && us_locked) {
		ast_channel_lock(us);
	}
	ao2_lock(p);

	return 0;
}
예제 #10
0
static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
{
	struct phone_pvt *p = ast->pvt->pvt;
	int res;
	int maxfr=0;
	char *pos;
	int sofar;
	int expected;
	int codecset = 0;
	char tmpbuf[4];
	/* Write a frame of (presumably voice) data */
	if (frame->frametype != AST_FRAME_VOICE) {
		if (frame->frametype != AST_FRAME_IMAGE)
			ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
		return 0;
	}
	if (!(frame->subclass &
		(AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW))) {
		ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
		return -1;
	}
#if 0
	/* If we're not in up mode, go into up mode now */
	if (ast->_state != AST_STATE_UP) {
		ast_setstate(ast, AST_STATE_UP);
		phone_setup(ast);
	}
#else
	if (ast->_state != AST_STATE_UP) {
		/* Don't try tos end audio on-hook */
		return 0;
	}
#endif	
	if (frame->subclass == AST_FORMAT_G723_1) {
		if (p->lastformat != AST_FORMAT_G723_1) {
			ioctl(p->fd, PHONE_PLAY_STOP);
			ioctl(p->fd, PHONE_REC_STOP);
			if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) {
				ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
				return -1;
			}
			if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
				ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
				return -1;
			}
			p->lastformat = AST_FORMAT_G723_1;
			p->lastinput = AST_FORMAT_G723_1;
			/* Reset output buffer */
			p->obuflen = 0;
			codecset = 1;
		}
		if (frame->datalen > 24) {
			ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen);
			return -1;
		}
		maxfr = 24;
	} else if (frame->subclass == AST_FORMAT_SLINEAR) {
		if (p->lastformat != AST_FORMAT_SLINEAR) {
			ioctl(p->fd, PHONE_PLAY_STOP);
			ioctl(p->fd, PHONE_REC_STOP);
			if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) {
				ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
				return -1;
			}
			if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
				ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
				return -1;
			}
			p->lastformat = AST_FORMAT_SLINEAR;
			p->lastinput = AST_FORMAT_SLINEAR;
			codecset = 1;
			/* Reset output buffer */
			p->obuflen = 0;
		}
		maxfr = 480;
	} else if (frame->subclass == AST_FORMAT_ULAW) {
		if (p->lastformat != AST_FORMAT_ULAW) {
			ioctl(p->fd, PHONE_PLAY_STOP);
			ioctl(p->fd, PHONE_REC_STOP);
			if (ioctl(p->fd, PHONE_PLAY_CODEC, ULAW)) {
				ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
				return -1;
			}
			if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) {
				ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
				return -1;
			}
			p->lastformat = AST_FORMAT_ULAW;
			p->lastinput = AST_FORMAT_ULAW;
			codecset = 1;
			/* Reset output buffer */
			p->obuflen = 0;
		}
		maxfr = 240;
	}
	if (codecset) {
		ioctl(p->fd, PHONE_REC_DEPTH, 3);
		ioctl(p->fd, PHONE_PLAY_DEPTH, 3);
		if (ioctl(p->fd, PHONE_PLAY_START)) {
			ast_log(LOG_WARNING, "Failed to start playback\n");
			return -1;
		}
		if (ioctl(p->fd, PHONE_REC_START)) {
			ast_log(LOG_WARNING, "Failed to start recording\n");
			return -1;
		}
	}
	/* If we get here, we have a voice frame of Appropriate data */
	sofar = 0;
	pos = frame->data;
	while(sofar < frame->datalen) {
		/* Write in no more than maxfr sized frames */
		expected = frame->datalen - sofar;
		if (maxfr < expected)
			expected = maxfr;
		/* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX 
		   we have to pad it to 24 bytes still.  */
		if (frame->datalen == 4) {
			if (p->silencesupression) {
				memset(tmpbuf + 4, 0, sizeof(tmpbuf) - 4);
				memcpy(tmpbuf, frame->data, 4);
				expected = 24;
				res = phone_write_buf(p, tmpbuf, expected, maxfr);
			}
			res = 4;
			expected=4;
		} else {
			res = phone_write_buf(p, pos, expected, maxfr);
		}
		if (res != expected) {
			if ((errno != EAGAIN) && (errno != EINTR)) {
				if (res < 0) 
					ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
	/*
	 * Card is in non-blocking mode now and it works well now, but there are
	 * lot of messages like this. So, this message is temporarily disabled.
	 */
#if 0
				else
					ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
#endif
				return -1;
			} else /* Pretend it worked */
				res = expected;
		}
		sofar += res;
		pos += res;
	}
	return 0;
}
예제 #11
0
static struct ast_frame  *phone_exception(struct ast_channel *ast)
{
	int res;
	union telephony_exception phonee;
	struct phone_pvt *p = ast->pvt->pvt;
	char digit;

	/* Some nice norms */
	p->fr.datalen = 0;
	p->fr.samples = 0;
	p->fr.data =  NULL;
	p->fr.src = type;
	p->fr.offset = 0;
	p->fr.mallocd=0;
	p->fr.delivery.tv_sec = 0;
	p->fr.delivery.tv_usec = 0;

	phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION);
	if (phonee.bits.dtmf_ready)  {
		if (option_debug)
			ast_log(LOG_DEBUG, "phone_exception(): DTMF\n");
	
		/* We've got a digit -- Just handle this nicely and easily */
		digit =  ioctl(p->fd, PHONE_GET_DTMF_ASCII);
		p->fr.subclass = digit;
		p->fr.frametype = AST_FRAME_DTMF;
		return &p->fr;
	}
	if (phonee.bits.hookstate) {
		if (option_debug)
			ast_log(LOG_DEBUG, "Hookstate changed\n");
		res = ioctl(p->fd, PHONE_HOOKSTATE);
		/* See if we've gone on hook, if so, notify by returning NULL */
		if (option_debug)
			ast_log(LOG_DEBUG, "New hookstate: %d\n", res);
		if (!res && (p->mode != MODE_FXO))
			return NULL;
		else {
			if (ast->_state == AST_STATE_RINGING) {
				/* They've picked up the phone */
				p->fr.frametype = AST_FRAME_CONTROL;
				p->fr.subclass = AST_CONTROL_ANSWER;
				phone_setup(ast);
				ast_setstate(ast, AST_STATE_UP);
				return &p->fr;
			}  else 
				ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->_state);
		}
	}
#if 1
	if (phonee.bits.pstn_ring)
		ast_verbose("Unit is ringing\n");
	if (phonee.bits.caller_id) {
		ast_verbose("We have caller ID\n");
	}
	if (phonee.bits.pstn_wink)
		ast_verbose("Detected Wink\n");
#endif
	/* Strange -- nothing there.. */
	p->fr.frametype = AST_FRAME_NULL;
	p->fr.subclass = 0;
	return &p->fr;
}
예제 #12
0
static struct ast_frame *bestdata_handle_escape(struct ast_modem_pvt *p, char esc)
{
	char name[30]="",nmbr[30]="";
	time_t	now;

	/* Handle escaped characters -- but sometimes we call it directly as 
	   a quick way to cause known responses */
	p->fr.frametype = AST_FRAME_NULL;
	p->fr.subclass = 0;
	p->fr.data = NULL;
	p->fr.datalen = 0;
	p->fr.samples = 0;
	p->fr.offset = 0;
	p->fr.mallocd = 0;
	p->fr.delivery.tv_sec = 0;
	p->fr.delivery.tv_usec = 0;
	if (esc)
		ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
	
	switch(esc) {
	case 'R': /* Pseudo ring */
		time(&now);
		if (now > (p->lastring + (RINGT / 1000)))
		   { /* if stale, treat as new */
			p->gotclid = 0;
		   }
		if (p->gotclid)
		   {
			p->fr.frametype = AST_FRAME_CONTROL;
			p->fr.subclass = AST_CONTROL_RING;
		   }
		p->ringt = RINGT;
		time(&p->lastring);
		return &p->fr;
	case 'X': /* Caller-ID Spill */
		if (p->gotclid) return &p->fr;
		name[0] = nmbr[0] = 0;
		for(;;)
		   {
			char res[1000]="";

			if (ast_modem_read_response(p, 5)) break;
			strncpy(res, p->response, sizeof(res)-1);
			ast_modem_trim(res);
			if (!strncmp(res,"\020.",2)) break;
			if (!strncmp(res,"NAME",4)) strncpy(name,res + 7, sizeof(name) - 1);
			if (!strncmp(res,"NMBR",4)) strncpy(nmbr,res + 7, sizeof(nmbr) - 1);
		   }
		p->gotclid = 1;
		if ((!strcmp(name,"O")) || (!strcmp(name,"P"))) name[0] = 0;
		if ((!strcmp(nmbr,"O")) || (!strcmp(nmbr,"P"))) nmbr[0] = 0;
		if ((name[0]) && (nmbr[0])) snprintf(p->cid,sizeof(p->cid),
			"\"%s\" <%s>",name,nmbr);
		else if (name[0]) snprintf(p->cid,sizeof(p->cid),
			"\"%s\"",name);
		else if (nmbr[0]) snprintf(p->cid,sizeof(p->cid),
			"%s",nmbr);
		if (p->owner) p->owner->callerid = strdup(p->cid);
		return &p->fr;
	case '@': /* response from "OK" in command mode */
		if (p->owner)
			ast_setstate(p->owner, AST_STATE_UP);
		if (bestdata_startrec(p)) return NULL;
		p->fr.frametype = AST_FRAME_CONTROL;
		p->fr.subclass = AST_CONTROL_RING;
		return &p->fr;
	case 'b': /* Busy signal */
		p->fr.frametype = AST_FRAME_CONTROL;
		p->fr.subclass = AST_CONTROL_BUSY;
		return &p->fr;
	case 'o': /* Overrun */
		ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
		if (ast_modem_send(p, "\0x10E", 2)) 
			ast_log(LOG_WARNING, "Unable to flush buffers\n");
		return &p->fr;	
	case '0':  /* All the DTMF characters */
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '*':
	case '#':
	case 'A':
	case 'B':
	case 'C':
	case 'D':
		p->dtmfrx = esc;  /* save this for when its done */
		return &p->fr;	
	case '/': /* Start of DTMF tone shielding */
		p->dtmfrx = ' ';
		return &p->fr;	
	case '~': /* DTMF transition to off */
		if (p->dtmfrx > ' ')
		   {
			p->fr.frametype = AST_FRAME_DTMF;
			p->fr.subclass = p->dtmfrx;
		   }
		p->dtmfrx = 0;
		return &p->fr;	
	case 'u': /* Underrun */
		ast_log(LOG_WARNING, "Data underrun\n");
		/* Fall Through */
	case CHAR_ETX: /* End Transmission */
	case 'd': /* Dialtone */
	case 'c': /* Calling Tone */
	case 'e': /* European version */
	case 'a': /* Answer Tone */
	case 'f': /* Bell Answer Tone */
	case 'T': /* Timing mark */
	case 't': /* Handset off hook */
	case 'h': /* Handset hungup */
	case 0: /* Pseudo signal */
		/* Ignore */
		return &p->fr;	
	default:
		ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
	}
	return &p->fr;
}