Example #1
0
static switch_status_t channel_on_destroy(switch_core_session_t *session)
{
    ctdm_private_t *tech_pvt = switch_core_session_get_private(session);
    
 	if ((tech_pvt = switch_core_session_get_private(session))) {
	
		if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_ENABLE_ECHOCANCEL, NULL)) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to enable echo cancellation.\n");
		}
        
		if (tech_pvt->read_codec.implementation) {
			switch_core_codec_destroy(&tech_pvt->read_codec);
		}
		
		if (tech_pvt->write_codec.implementation) {
			switch_core_codec_destroy(&tech_pvt->write_codec);
		}

		switch_core_session_unset_read_codec(session);
		switch_core_session_unset_write_codec(session);
        
        ftdm_channel_close(&tech_pvt->ftdm_channel);
	}

    return SWITCH_STATUS_SUCCESS;
}
void sngisdn_delayed_disconnect(void* p_sngisdn_info)
{
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info;	
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;	

	ftdm_mutex_lock(ftdmchan->mutex);
	if (ftdmchan->caller_data.hangup_cause == IN_CCNORTTODEST || ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending delayed DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n",
									signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId);

 		sngisdn_snd_disconnect(ftdmchan);
		if (ftdmchan->caller_data.hangup_cause == IN_CCNORTTODEST) {
			ftdm_channel_t *close_chan = ftdmchan;
			ftdm_channel_close(&close_chan);
		}
	}

	ftdm_mutex_unlock(ftdmchan->mutex);
	return;
}
Example #3
0
static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan)
{

	ftdm_log_chan(ftdmchan, STATE_ADVANCE_LOG_LEVEL , "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
	
	ftdm_channel_complete_state(ftdmchan);

		switch (ftdmchan->state) {

		/* starting an incoming call */
		case FTDM_CHANNEL_STATE_COLLECT: 
			{
				
				
			}
			break;

			/* starting an outgoing call */
		case FTDM_CHANNEL_STATE_DIALING:
			{
				uint32_t interval = 0;
				
				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval);

				{

					ftdm_gsm_span_data_t *gsm_data;
					gsm_data = ftdmchan->span->signal_data;
					gsm_data->call_id = g_outbound_call_id++;				
					wat_con_event_t con_event;
					memset(&con_event, 0, sizeof(con_event));
					ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits);
					ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits);
					wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event);
 
					SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING);
					
					
				}

				
			}
			break;

			/* incoming call was offered */
		case FTDM_CHANNEL_STATE_RING:

			/* notify the user about the new call */

			ftdm_log(FTDM_LOG_INFO, "Answering Incomming Call\r\n");
			SEND_STATE_SIGNAL(FTDM_SIGEVENT_START);
			
			break;

			/* the call is making progress */
		case FTDM_CHANNEL_STATE_PROGRESS:
		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			{
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA);
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
			}
			break;

			/* the call was answered */
		case FTDM_CHANNEL_STATE_UP:
			{
				if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
				
					SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP);
				}
				else {
						ftdm_gsm_span_data_t *gsm_data;
						gsm_data = ftdmchan->span->signal_data;
						wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); 
				}
				
				
			}
			break;

			/* just got hangup */
		case FTDM_CHANNEL_STATE_HANGUP:
			{
				ftdm_gsm_span_data_t *gsm_data;
				gsm_data = ftdmchan->span->signal_data;
				wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id);
				gsm_data->call_id = 0;
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			break;

		case FTDM_CHANNEL_STATE_TERMINATING:
			{
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			break;

			/* finished call for good */
		case FTDM_CHANNEL_STATE_DOWN: 
			{
				ftdm_channel_t *closed_chan;
				closed_chan = ftdmchan;
				ftdm_channel_close(&closed_chan);
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n");
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			break;

			/* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
		case FTDM_CHANNEL_STATE_RINGING: 
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
			SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING);
				
			break;

			/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
		case FTDM_CHANNEL_STATE_RESET:
			{
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
			}
			break;

		default:
			{
				ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
			}
			break;
	}

	
	return FTDM_SUCCESS;
}
Example #4
0
int main(int argc, char *argv[])
{
	ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
	ftdm_channel_t *chan;
	unsigned ms = 20;
	ftdm_codec_t codec = FTDM_CODEC_SLIN;
	unsigned runs = 1;
	int spanid, chanid;

	ftdm_unused_arg(argc);
	ftdm_unused_arg(argv);

	if (ftdm_global_init() != FTDM_SUCCESS) {
		fprintf(stderr, "Error loading FreeTDM\n");
		exit(-1);
	}

	printf("FreeTDM loaded\n");

 top:
	//if (ftdm_channel_open_any("wanpipe", 0, FTDM_TOP_DOWN, &chan) == FTDM_SUCCESS) {
	if (ftdm_channel_open(1, 1, &chan) == FTDM_SUCCESS) {
		int x = 0;
		spanid = ftdm_channel_get_span_id(chan);
		chanid = ftdm_channel_get_id(chan);
		printf("opened channel %d:%d\n", spanid, chanid);

#if 1
		if (ftdm_channel_command(chan, FTDM_COMMAND_SET_INTERVAL, &ms) == FTDM_SUCCESS) {
			ms = 0;
			ftdm_channel_command(chan, FTDM_COMMAND_GET_INTERVAL, &ms);
			printf("interval set to %u\n", ms);
		} else {
			printf("set interval failed [%s]\n", ftdm_channel_get_last_error(chan));
		}
#endif
		if (ftdm_channel_command(chan, FTDM_COMMAND_SET_CODEC, &codec) == FTDM_SUCCESS) {
			codec = 1;
			ftdm_channel_command(chan, FTDM_COMMAND_GET_CODEC, &codec);
			printf("codec set to %u\n", codec);
		} else {
			printf("set codec failed [%s]\n", ftdm_channel_get_last_error(chan));
		}

		for(x = 0; x < 25; x++) {
			unsigned char buf[2048];
			ftdm_size_t len = sizeof(buf);
			ftdm_wait_flag_t flags = FTDM_READ;

			if (ftdm_channel_wait(chan, &flags, -1) == FTDM_FAIL) {
				printf("wait FAIL! %u [%s]\n", (unsigned)len, ftdm_channel_get_last_error(chan));
			}
			if (flags & FTDM_READ) {
				if (ftdm_channel_read(chan, buf, &len) == FTDM_SUCCESS) {
					printf("READ: %u\n", (unsigned)len); 
				} else {
					printf("READ FAIL! %u [%s]\n", (unsigned)len, ftdm_channel_get_last_error(chan));
					break;
				}
			} else {
				printf("wait fail [%s]\n", ftdm_channel_get_last_error(chan));
			}
		}
		ftdm_channel_close(&chan);
	} else {
		printf("open fail [%s]\n", ftdm_channel_get_last_error(chan));
	}

	if(--runs) {
		goto top;
	}

	ftdm_global_destroy();
	return 0;
}
Example #5
0
static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
													switch_caller_profile_t *outbound_profile,
													switch_core_session_t **new_session, 
													switch_memory_pool_t **pool,
													switch_originate_flag_t flags, switch_call_cause_t *cancel_cause)
{
    const char  *szchanid = switch_event_get_header(var_event, kCHAN_ID),
                *span_name = switch_event_get_header(var_event, kSPAN_NAME),
                *szprebuffer_len = switch_event_get_header(var_event, kPREBUFFER_LEN);
    int chan_id;
    int span_id;
    switch_caller_profile_t *caller_profile;
    ftdm_span_t *span;
    ftdm_channel_t *chan;
    switch_channel_t *channel;
    char name[128];
    const char *dname;
    ftdm_codec_t codec;
    uint32_t interval;
    ctdm_private_t *tech_pvt = NULL;

    if (zstr(szchanid) || zstr(span_name)) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Both ["kSPAN_ID"] and ["kCHAN_ID"] have to be set.\n");
        goto fail;
    }
    
    chan_id = atoi(szchanid);
    
    if (ftdm_span_find_by_name(span_name, &span) == FTDM_SUCCESS) {
         span_id = ftdm_span_get_id(span);   
    } else {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find span [%s]\n", span_name);
        goto fail;
    }

    if (!(*new_session = switch_core_session_request(ctdm.endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 0, pool))) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't request session.\n");
        goto fail;
    }
    
    channel = switch_core_session_get_channel(*new_session);
    
    if (ftdm_channel_open_ph(span_id, chan_id, &chan) != FTDM_SUCCESS) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open span or channel.\n"); 
        goto fail;
    }
    
    span = ftdm_channel_get_span(chan);
    
    tech_pvt = switch_core_session_alloc(*new_session, sizeof *tech_pvt);
    tech_pvt->chan_id = chan_id;
    tech_pvt->span_id = span_id;
    tech_pvt->ftdm_channel = chan;
    tech_pvt->session = *new_session;
    tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
    tech_pvt->read_frame.data = tech_pvt->databuf;
    tech_pvt->prebuffer_len = zstr(szprebuffer_len) ? 0 : atoi(szprebuffer_len);
    switch_core_session_set_private(*new_session, tech_pvt);
    
    
    caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
    switch_channel_set_caller_profile(channel, caller_profile);
    
    snprintf(name, sizeof(name), "tdm/%d:%d", span_id, chan_id);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name);
	switch_channel_set_name(channel, name);
    
    switch_channel_set_state(channel, CS_INIT);
    
	if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_GET_CODEC, &codec)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel codec.\n");
		return SWITCH_STATUS_GENERR;
	}
    
    if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_GET_INTERVAL, &interval)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel interval.\n");
		return SWITCH_STATUS_GENERR;
	}
    
    if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tech_pvt->prebuffer_len)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set channel pre buffer size.\n");
		return SWITCH_STATUS_GENERR;        
    }

    if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_ENABLE_ECHOCANCEL, NULL)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to set enable echo cancellation.\n");
	}
    
	switch(codec) {
        case FTDM_CODEC_ULAW:
		{
			dname = "PCMU";
		}
            break;
        case FTDM_CODEC_ALAW:
		{
			dname = "PCMA";
		}
            break;
        case FTDM_CODEC_SLIN:
		{
			dname = "L16";
		}
            break;
        default:
		{
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid codec value retrieved from channel, codec value: %d\n", codec);
			goto fail;
		}
	}

    
	if (switch_core_codec_init(&tech_pvt->read_codec,
							   dname,
							   NULL,
							   8000,
							   interval,
							   1,
							   SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
							   NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
        goto fail;
	} else {
		if (switch_core_codec_init(&tech_pvt->write_codec,
								   dname,
								   NULL,
								   8000,
								   interval,
								   1,
								   SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
								   NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
			switch_core_codec_destroy(&tech_pvt->read_codec);
            goto fail;
		}
	}
    
    if (switch_core_session_set_read_codec(*new_session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n");
        goto fail;
    }
    
    if (switch_core_session_set_write_codec(*new_session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n");        
    }
    
    if (switch_core_session_thread_launch(*new_session) != SWITCH_STATUS_SUCCESS) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't start session thread.\n"); 
        goto fail;
    }
    
    switch_channel_mark_answered(channel);
    
    return SWITCH_CAUSE_SUCCESS;

fail:
    
    if (tech_pvt) {
        if (tech_pvt->ftdm_channel) {
            ftdm_channel_close(&tech_pvt->ftdm_channel);
        }
        
        if (tech_pvt->read_codec.implementation) {
			switch_core_codec_destroy(&tech_pvt->read_codec);
		}
		
		if (tech_pvt->write_codec.implementation) {
			switch_core_codec_destroy(&tech_pvt->write_codec);
		}
    }
    
    if (*new_session) {
        switch_core_session_destroy(new_session);
    }
    return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
}
Example #6
0
static void launch_channel(struct sangoma_pri *spri, int channo)
{
    pid_t pid;
    int fd = 0, file = 0, inlen = 0, outlen = 0;
    unsigned char inframe[MAX_BYTES], outframe[MAX_BYTES];
    fd_set readfds;
    int mtu_mru=BYTES / 2;
    int err;
    ftdm_channel_t *chan;
    ftdm_codec_t codec = FTDM_CODEC_SLIN;
    unsigned ms = 20;
    unsigned int lead = 50;
    int ifd = -1;
    ftdm_tone_type_t tt = FTDM_TONE_DTMF;
    char dtmf[] = "1234567890";
    int loops = 0;

    pid = fork();

    if (pid) {
        pidmap[channo-1].pid = pid;
        printf("-- Launching process %d to handle channel %d\n", pid, channo);
        return;
    }

    signal(SIGINT, handle_SIGINT);

    //ifd = open("/nfs/sounds/ptest.raw", O_WRONLY|O_CREAT|O_TRUNC, 777);

    memset(inframe, 0, MAX_BYTES);
    memset(outframe, 0, MAX_BYTES);

    if (ftdm_channel_open(spri->span, channo, &chan) != FTDM_SUCCESS) {
        printf("DEBUG cant open fd!\n");
    }



#if 1
    if (ftdm_channel_command(chan, FTDM_COMMAND_SET_CODEC, &codec) != FTDM_SUCCESS) {
        printf("Critical Error: Failed to set driver codec!\n");
        ftdm_channel_close(&chan);
        exit(-1);
    }
#endif

#if 1
    if (ftdm_channel_command(chan, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) {
        printf("Critical Error: Failed to set dtmf detect!\n");
        ftdm_channel_close(&chan);
        exit(-1);
    }
    ftdm_channel_set_event_callback(chan, my_ftdm_event_handler);
#endif


    if (ftdm_channel_command(chan, FTDM_COMMAND_SET_INTERVAL, &ms) != FTDM_SUCCESS) {
        printf("Critical Error: Failed to set codec interval!\n");
        ftdm_channel_close(&chan);
        exit(-1);
    }

    file = open("sound.raw", O_RDONLY);
    if (file < 0) {
        printf("Critical Error: Failed to open sound file!\n");
        ftdm_channel_close(&chan);
        exit(-1);
    }


    while(ready) {
        ftdm_wait_flag_t flags = FTDM_READ;
        ftdm_size_t len;
        loops++;

        if (lead) {
            lead--;
        }

        if (!lead && loops == 300) {
#if 1
            if (ftdm_channel_command(chan, FTDM_COMMAND_SEND_DTMF, dtmf) != FTDM_SUCCESS) {
                printf("Critical Error: Failed to send dtmf\n");
                ftdm_channel_close(&chan);
                exit(-1);
            }
#endif

        }

        if (ftdm_channel_wait(chan, &flags, 2000) != FTDM_SUCCESS) {
            printf("wait FAIL! [%s]\n", chan->last_error);
            break;
        }

        if (flags & FTDM_READ) {
            len = MAX_BYTES;
            if (ftdm_channel_read(chan, inframe, &len) == FTDM_SUCCESS) {
                //printf("READ: %d\n", len);
                //write(ifd, inframe, len);
                if(!lead && (outlen = read(file, outframe, len)) <= 0) {
                    break;
                }

            } else {
                printf("READ FAIL! %d [%s]\n", len, chan->last_error);
                break;
            }
            if (lead) {
                continue;
            }
            ftdm_channel_write(chan, outframe, sizeof(outframe), &len);
        } else {
            printf("BREAK");
            break;
        }
    }

    printf("loop done\n");

    //sangoma_get_full_cfg(fd, &tdm_api);
    close(file);
    //close(ifd);

    pri_hangup(spri->pri, channo, 16);
    if (ftdm_channel_close(&chan) != FTDM_SUCCESS) {
        printf("Critical Error: Failed to close channel [%s]\n", chan->last_error);
    }

    printf("Call Handler: Process Finished\n");
    exit(0);
}
Example #7
0
static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
{
    ftdm_status_t status;
    ftdm_sigmsg_t sig;
    pritap_t *pritap = ftdmchan->span->signal_data;
    pritap_t *peer_pritap = pritap->peerspan->signal_data;

    ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "processing state %s\n", ftdm_channel_state2str(ftdmchan->state));

    memset(&sig, 0, sizeof(sig));
    sig.chan_id = ftdmchan->chan_id;
    sig.span_id = ftdmchan->span_id;
    sig.channel = ftdmchan;

    ftdm_channel_complete_state(ftdmchan);

    switch (ftdmchan->state) {
    case FTDM_CHANNEL_STATE_DOWN:
    {
        ftdm_channel_t *fchan = ftdmchan;

        /* Destroy the peer data first */
        if (fchan->call_data) {
            ftdm_channel_t *peerchan = fchan->call_data;
            ftdm_channel_t *pchan = peerchan;

            ftdm_channel_lock(peerchan);

            pchan->call_data = NULL;
            pchan->pflags = 0;
            ftdm_channel_close(&pchan);

            ftdm_channel_unlock(peerchan);
        } else {
            ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "No call data?\n");
        }

        ftdmchan->call_data = NULL;
        ftdmchan->pflags = 0;
        ftdm_channel_close(&fchan);
    }
    break;

    case FTDM_CHANNEL_STATE_PROGRESS:
    case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
    case FTDM_CHANNEL_STATE_HANGUP:
        break;

    case FTDM_CHANNEL_STATE_UP:
    {
        if (ftdm_test_pflag(ftdmchan, PRITAP_NETWORK_ANSWER)) {
            ftdm_clear_pflag(ftdmchan, PRITAP_NETWORK_ANSWER);
            sig.event_id = FTDM_SIGEVENT_UP;
            ftdm_span_send_signal(ftdmchan->span, &sig);
        }
    }
    break;

    case FTDM_CHANNEL_STATE_RING:
    {
        sig.event_id = FTDM_SIGEVENT_START;
        /* The ring interface (where the setup was received) is the peer, since we RING the channel
         * where PROCEED/PROGRESS is received */
        ftdm_sigmsg_add_var(&sig, "pritap_ring_interface", PRITAP_GET_INTERFACE(peer_pritap->iface));
        if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) {
            ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
        }
    }
    break;

    case FTDM_CHANNEL_STATE_TERMINATING:
    {
        if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP) {
            sig.event_id = FTDM_SIGEVENT_STOP;
            status = ftdm_span_send_signal(ftdmchan->span, &sig);
        }
        ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
    }
    break;

    default:
    {
        ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "ignoring state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(ftdmchan->state));
    }
    break;
    }

    return FTDM_SUCCESS;
}