/* NMT protocol handling
 *
 * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
 * All Rights Reserved
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../common/debug.h"
#include "../common/timer.h"
#include "../common/call.h"
#include "../common/cause.h"
#include "nmt.h"
#include "dsp.h"
#include "frame.h"

/* Call reference for calls from mobile station to network
   This offset of 0x400000000 is required for MNCC interface. */
static int new_callref = 0x40000000;

/* Timers */
#define RELEASE_TO	2.0	/* how long do we wait for release guard of the phone */
#define DIALING_TO	1.0	/* if we have a pause during dialing, we abort the call */
#define CHANNEL_TO	2.0	/* how long do we wait for phone to appear on assigned channel */
#define RINGING_TO	60.0	/* how long may the phone ring */
#define SUPERVISORY_TO1	3.0	/* 3 sec to detect after setup */
#define SUPERVISORY_TO2	20.0	/* 20 sec lost until abort */

/* Counters */
#define PAGE_TRIES	3	/* How many time do we try to page the phone */

const char *nmt_state_name(enum nmt_state state)
{
	static char invalid[16];

	switch (state) {
	case STATE_NULL:
		return "(NULL)";
	case STATE_IDLE:
		return "IDLE";
	case STATE_ROAMING_IDENT:
		return "ROAMING IDENT";
	case STATE_ROAMING_CONFIRM:
		return "ROAMING CONFIRM";
	case STATE_MO_IDENT:
		return "MO CALL IDENT";
	case STATE_MO_CONFIRM:
		return "MO CALL CONFIRM";
	case STATE_MO_DIALING:
		return "MO CALL DIALING";
	case STATE_MO_COMPLETE:
		return "MO CALL COMPLETE";
	case STATE_MT_PAGING:
		return "MT CALL PAGING";
	case STATE_MT_CHANNEL:
		return "MT CALL CHANNEL";
	case STATE_MT_IDENT:
		return "MT CALL IDENT";
	case STATE_MT_RINGING:
		return "MT CALL RINGING";
	case STATE_MT_COMPLETE:
		return "MT CALL COMPLETE";
	case STATE_ACTIVE:
		return "ACTIVE";
	case STATE_MO_RELEASE:
		return "RELEASE MTX->MS";
	case STATE_MT_RELEASE:
		return "RELEASE MTX->MS";
	}

	sprintf(invalid, "invalid(%d)", state);
	return invalid;
}

static void nmt_new_state(nmt_t *nmt, enum nmt_state new_state)
{
	if (nmt->state == new_state)
		return;
	PDEBUG(DNMT, DEBUG_DEBUG, "State change: %s -> %s\n", nmt_state_name(nmt->state), nmt_state_name(new_state));
	nmt->state = new_state;
}

static struct nmt_channels {
	enum nmt_chan_type chan_type;
	const char *short_name;
	const char *long_name;
} nmt_channels[] = {
	{ CHAN_TYPE_CC,		"CC",	"calling channel" },
	{ CHAN_TYPE_TC,		"TC",	"traffic channel" },
	{ CHAN_TYPE_CC_TC,	"CC/TC","combined calling & traffic channel" },
	{ CHAN_TYPE_TEST,	"TEST",	"test channel" },
	{ 0, NULL, NULL }
};

void nmt_channel_list(void)
{
	int i;

	printf("Type\tDescription\n");
	printf("------------------------------------------------------------------------\n");
	for (i = 0; nmt_channels[i].long_name; i++)
		printf("%s\t%s\n", nmt_channels[i].short_name, nmt_channels[i].long_name);
}

int nmt_channel_by_short_name(const char *short_name)
{
	int i;

	for (i = 0; nmt_channels[i].short_name; i++) {
		if (!strcasecmp(nmt_channels[i].short_name, short_name)) {
			PDEBUG(DNMT, DEBUG_INFO, "Selecting channel '%s' = %s\n", nmt_channels[i].short_name, nmt_channels[i].long_name);
			return nmt_channels[i].chan_type;
		}
	}

	return -1;
}

const char *chan_type_short_name(enum nmt_chan_type chan_type)
{
	int i;

	for (i = 0; nmt_channels[i].short_name; i++) {
		if (nmt_channels[i].chan_type == chan_type)
			return nmt_channels[i].short_name;
	}

	return "invalid";
}

const char *chan_type_long_name(enum nmt_chan_type chan_type)
{
	int i;

	for (i = 0; nmt_channels[i].long_name; i++) {
		if (nmt_channels[i].chan_type == chan_type)
			return nmt_channels[i].long_name;
	}

	return "invalid";
}

const char *nmt_dir_name(enum nmt_direction dir)
{
	switch (dir) {
	case MTX_TO_MS:
		return "MTX->MS";
	case MTX_TO_BS:
		return "MTX->BS";
	case MTX_TO_XX:
		return "MTX->XX";
	case BS_TO_MTX:
		return "BS->MTX";
	case MS_TO_MTX:
		return "MS->MTX";
	case XX_TO_MTX:
		return "XX->MTX";
	}
	return "invalid";
}

/* Convert channel number to frequency number of base station.
   Set 'uplink' to 1 to get frequency of mobile station. */
double nmt_channel2freq(int channel, int uplink)
{
	double freq;

	if (channel < 1)
		return 0;
	else if (channel <= 180)
		freq = 463.000 + (channel - 1) * 0.025;
	else if (channel <= 200)
		freq = 462.500 + (channel - 181) * 0.025;
	else if (channel <= 380)
		freq = 463.000 + (channel - 201) * 0.025 + 0.0125;
	else if (channel <= 399) /* no channel 400, caused by interleaving and coding */
		freq = 462.500 + (channel - 381) * 0.025 + 0.0125;
	else
		return 0;

	if (uplink)
		freq -= 10.000;

	return freq;
}

/* convert 7-digits dial string to NMT number */
static int dialstring2number(const char *dialstring, char *ms_country, char *ms_number)
{
	if (strlen(dialstring) != 7) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Wrong number of digits, use 7 digits: ZXXXXXX (Z=country, X=mobile number)\n");
		return -1;
	}
	if (dialstring[0] < '0' && dialstring[0] > '9') {
		PDEBUG(DNMT, DEBUG_NOTICE, "Invalid country digit (first digit) of dial string\n");
		return -1;
	}
	*ms_country = dialstring[0];
	memcpy(ms_number, dialstring + 1, 6);
	return 0;
}

/* country selector */
static struct nmt_country {
	int y;
	const char *short_name;
	const char *long_name;
	const char *provider_name;
} nmt_country[] = {
	{ 5, "DK",	"Denmark",		"Tele Danmark Mobile" },
	{ 6, "SE",	"Sweden",		"Telia Mobitel" },
	{ 7, "NO",	"Norway",		"Telenor Mobil" },
	{ 8, "FI",	"Finland",		"Telecom Finland" },
	{ 8, "IS",	"Iceland",		"Post & Telecom" },
	{ 5, "FO",	"Faroe Island",		"Faroese Telecom" },
	{ 7, "EE",	"Estonia",		"Eesti Mobiiltelefon" },
	{ 5, "LV",	"Latvia",		"Latvian Mobile Telephone" },
	{ 8, "LT",	"Lithuania",		"COMLIET" },
	{ 6, "BY",	"Belarus",		"Belcel" },
	{ 5, "MO",	"OSS/Moscow",		"Moscow Cellular Comm." },
	{ 6, "STP",	"OSS/St Petersburg",	"Delta Telecom" },
	{ 6, "STP",	"OSS/Leningrads Dist.",	"Delta Telecom" },
	{ 7, "CAR",	"OSS/Carelian Rep.",	"Telecom Finland" },
	{ 5, "MUR",	"OSS/Murmansk",		"Telecom Finland" },
	{ 5, "LED",	"OSS/Leningrads Dist.",	"Telecom Finland" },
	{ 5, "KAL",	"Kaliningrad",		"Telecom Finland" },
	{ 7, "PL",	"Poland",		"CENTERTEL" },
	{ 6, "BG",	"Bulgaria",		"MOBIFON" },
	{ 5, "RO",	"Romania",		"Telefonica Romania" },
	{ 6, "UA",	"Ukraine",		"Ukraine Mobile Comm." },
	{ 1, "RU1",	"",			"" },
	{ 2, "RU2",	"",			"" },
	{ 3, "RU3",	"",			"" },
	{ 4, "RU4",	"",			"" },
	{ 0, NULL, NULL, NULL }
};

void nmt_country_list(void)
{
	int i;

	printf("TA\tShort\tCountry (Provider)\n");
	printf("------------------------------------------------------------------------\n");
	for (i = 0; nmt_country[i].short_name; i++) {
		if (nmt_country[i].long_name[0])
			printf("%d\t%s\t%s (%s)\n", nmt_country[i].y, nmt_country[i].short_name, nmt_country[i].long_name, nmt_country[i].provider_name);
		else
			printf("%d\t%s\n", nmt_country[i].y, nmt_country[i].short_name);
	}
}

uint8_t nmt_country_by_short_name(const char *short_name)
{
	int i;

	for (i = 0; nmt_country[i].short_name; i++) {
		if (!strcasecmp(nmt_country[i].short_name, short_name)) {
			PDEBUG(DNMT, DEBUG_INFO, "Selecting country code %d of %s,%s (provider '%s')\n", nmt_country[i].y, nmt_country[i].short_name, nmt_country[i].long_name, nmt_country[i].provider_name);
			return nmt_country[i].y;
		}
	}

	return 0;
}

static void nmt_timeout(struct timer *timer);
static void nmt_go_idle(nmt_t *nmt);

/* Create transceiver instance and link to a list. */
int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compander, int supervisory, int loopback)
{
	nmt_t *nmt;
	int rc;

	if (channel < 1 || channel > 399) {
		PDEBUG(DNMT, DEBUG_ERROR, "Channel number %d invalid.\n", channel);
		return -EINVAL;
	}

	if (channel >= 201) {
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Channels numbers above 200 have been specified, but never used. These 'interleaved channels are probably not supports by the phone.\n");
	}

	if (channel >= 181 && channel <= 200) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Extended channel numbers (181..200) have been specified, but never been supported for sure. There is no phone to test with, so don't use it!\n");
	}

	if (chan_type == CHAN_TYPE_CC) {
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel can be used for calling only.\n");
		PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the mobile phone is possible on this channel.\n");
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
	}
	if (chan_type == CHAN_TYPE_TC) {
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel can be used for traffic only.\n");
		PDEBUG(DNMT, DEBUG_NOTICE, "*** No call to the mobile phone is possible on this channel.\n");
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
	}
	if (chan_type == CHAN_TYPE_TEST && !loopback) {
		PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel can be used for nothing but testing signal decoder.\n");
	}

	nmt = calloc(1, sizeof(nmt_t));
	if (!nmt) {
		PDEBUG(DNMT, DEBUG_ERROR, "No memory!\n");
		return -ENOMEM;
	}

	PDEBUG(DNMT, DEBUG_DEBUG, "Creating 'NMT' instance for channel = %d (sample rate %d).\n", channel, samplerate);

	/* init general part of transceiver */
	rc = sender_create(&nmt->sender, channel, sounddev, samplerate, cross_channels, rx_gain, pre_emphasis, de_emphasis, write_wave, read_wave, loopback, 0, -1);
	if (rc < 0) {
		PDEBUG(DNMT, DEBUG_ERROR, "Failed to init transceiver process!\n");
		goto error;
	}

	/* init audio processing */
	rc = dsp_init_sender(nmt);
	if (rc < 0) {
		PDEBUG(DNMT, DEBUG_ERROR, "Failed to init audio processing!\n");
		goto error;
	}

	timer_init(&nmt->timer, nmt_timeout, nmt);
	nmt->sysinfo.chan_type = chan_type;
	nmt->sysinfo.ms_power = ms_power;
	nmt->sysinfo.traffic_area = traffic_area;
	nmt->sysinfo.area_no = area_no;
	nmt->compander = compander;
	nmt->supervisory = supervisory;

	/* go into idle state */
	nmt_go_idle(nmt);

	return 0;

error:
	nmt_destroy(&nmt->sender);

	return rc;
}

/* Destroy transceiver instance and unlink from list. */
void nmt_destroy(sender_t *sender)
{
	nmt_t *nmt = (nmt_t *) sender;

	PDEBUG(DNMT, DEBUG_DEBUG, "Destroying 'NMT' instance for channel = %d.\n", sender->kanal);
	dsp_cleanup_sender(nmt);
	timer_exit(&nmt->timer);
	sender_destroy(&nmt->sender);
	free(nmt);
}

/* Abort connection towards mobile station by sending idle digits. */
static void nmt_go_idle(nmt_t *nmt)
{
	timer_stop(&nmt->timer);
	nmt->page_for_nmt = NULL;

	PDEBUG(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type));
	nmt_new_state(nmt, STATE_IDLE);
	nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
	memset(&nmt->subscriber, 0, sizeof(nmt->subscriber));
	memset(&nmt->dialing, 0, sizeof(nmt->dialing));
}

/* release an ongoing connection, this is used by roaming update and release initiated by MTX */
static void nmt_release(nmt_t *nmt)
{
	timer_stop(&nmt->timer);

	PDEBUG(DNMT, DEBUG_INFO, "Releasing connection towards mobile station.\n");
	nmt_new_state(nmt, STATE_MT_RELEASE);
	nmt->tx_frame_count = 0;
	nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
	timer_start(&nmt->timer, RELEASE_TO);
}

/* Enter paging state and transmit phone's number on calling channel */
static void nmt_page(nmt_t *nmt, char ms_country, const char *ms_number, int try)
{
	sender_t *sender;
	nmt_t *other;

	PDEBUG(DNMT, DEBUG_INFO, "Entering paging state (try %d), sending call to '%c,%s'.\n", try, ms_country, ms_number);
	nmt->subscriber.country = ms_country;
	strcpy(nmt->subscriber.number, ms_number);
	nmt->page_try = try;
	/* page on all CC (CC/TC) */
	for (sender = sender_head; sender; sender = sender->next) {
		other = (nmt_t *)sender;
		if (other == nmt) {
			/* this is us */
			PDEBUG(DNMT, DEBUG_INFO, "Paging on our channel %d.\n", other->sender.kanal);
		} else {
			/* this is not us */
			if (other->sysinfo.chan_type != CHAN_TYPE_CC
			 && other->sysinfo.chan_type != CHAN_TYPE_CC_TC)
				continue;
			if (other->state != STATE_IDLE)
				continue;
			PDEBUG(DNMT, DEBUG_INFO, "Paging on other channel %d.\n", other->sender.kanal);
			other->page_for_nmt = nmt;
		}
		nmt_new_state(other, STATE_MT_PAGING);
		nmt_set_dsp_mode(other, DSP_MODE_FRAME);
		other->tx_frame_count = 0;
		other->mt_channel = nmt->sender.kanal; /* ! channel from us (nmt->...) */
	}
}

/*
 * frame matching functions to check if channels is accessed correctly
 */

/* check match channel no, area no and traffic area */
static int match_channel(nmt_t *nmt, frame_t *frame)
{
	int channel, power;

	/* check channel match */
	nmt_decode_channel(frame->channel_no, &channel, &power);
	if (channel != nmt->sender.kanal) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Frame for different channel %d received, ignoring.\n", channel);
		return 0;
	}

	return 1;
}

static int match_area(nmt_t *nmt, frame_t *frame)
{
	uint8_t area_no, traffic_area;

	/* old phones do not support ZY digits */
	if (frame->area_info + frame->traffic_area == 0)
		goto skip_area;

	area_no = frame->area_info >> 2;
	if (area_no == 0 && nmt->sysinfo.area_no != 0)
		area_no = 4;
	if (area_no != nmt->sysinfo.area_no) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Received area no (%d) does not match the base station's area no (%d), ignoring.\n", area_no, nmt->sysinfo.area_no);
		return 0;
	}

	traffic_area = ((frame->area_info & 0x3) << 4) | frame->traffic_area;
	if (nmt->sysinfo.traffic_area != 0 && (nmt->sysinfo.traffic_area & 0x3f) != traffic_area) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Received 6 bits of traffic area (0x%02x) does not match the 6 bits of base station's traffic area (0x%02x), ignoring.\n", nmt->sysinfo.traffic_area & 0x3f, traffic_area);
		return 0;
	}
skip_area:

	return 1;
}

/* check match subscriber number */
static int match_subscriber(nmt_t *nmt, frame_t *frame)
{
	if (nmt_digits2value(&nmt->subscriber.country, 1) != frame->ms_country) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Received non matching subscriber counrtry, ignoring.\n");
		return 0;
	}
	if (nmt_digits2value(nmt->subscriber.number, 6) != frame->ms_number) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Received non matching subscriber number, ignoring.\n");
		return 0;
	}

	return 1;
}

/*
 * handle idle channel
 */

static void tx_idle(nmt_t *nmt, frame_t *frame)
{
	switch (nmt->sysinfo.chan_type) {
	case CHAN_TYPE_CC:
		frame->index = NMT_MESSAGE_1a;
		break;
	case CHAN_TYPE_TC:
		frame->index = NMT_MESSAGE_4;
		break;
	case CHAN_TYPE_CC_TC:
		frame->index = NMT_MESSAGE_1b;
		break;
	case CHAN_TYPE_TEST:
		frame->index = NMT_MESSAGE_30;
		break;
	}
	frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
	frame->traffic_area = nmt->sysinfo.traffic_area;
	frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
}

static void rx_idle(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_11a: /* roaming update and seizure */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		/* set subscriber */
		nmt_value2digits(frame->ms_country, &nmt->subscriber.country, 1);
		nmt_value2digits(frame->ms_number, nmt->subscriber.number, 6);
		nmt->subscriber.number[6] = '\0';

		PDEBUG(DNMT, DEBUG_INFO, "Received roaming seizure from subscriber %c,%s\n", nmt->subscriber.country, nmt->subscriber.number);
		/* change state */
		nmt_new_state(nmt, STATE_ROAMING_IDENT);
		nmt->rx_frame_count = 0;
		nmt->tx_frame_count = 0;
		break;
	case NMT_MESSAGE_10b: /* seizure from ordinary MS */
	case NMT_MESSAGE_12: /* seizure from coinbox MS */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		/* set subscriber */
		nmt_value2digits(frame->ms_country, &nmt->subscriber.country, 1);
		nmt_value2digits(frame->ms_number, nmt->subscriber.number, 6);
		if (frame->index == NMT_MESSAGE_12)
			nmt->subscriber.coinbox = 1;
		nmt->subscriber.number[6] = '\0';

		PDEBUG(DNMT, DEBUG_INFO, "Received call from subscriber %c,%s%s\n", nmt->subscriber.country, nmt->subscriber.number, (nmt->subscriber.coinbox) ? " (coinbox)" : "");
		/* change state */
		nmt_new_state(nmt, STATE_MO_IDENT);
		nmt->rx_frame_count = 0;
		nmt->tx_frame_count = 0;
		break;
	/* signals after release */
	case NMT_MESSAGE_13a: /* line signal */
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

/*
 * handle roaming
 */

static void tx_ident(nmt_t *nmt, frame_t *frame)
{
	frame->index = NMT_MESSAGE_3b;
	frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
	frame->traffic_area = nmt->sysinfo.traffic_area;
	frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1);
	frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6);
	frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
}

static void set_line_signal(nmt_t *nmt, frame_t *frame, uint8_t signal)
{
	frame->index = NMT_MESSAGE_5a;
	frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
	frame->traffic_area = nmt->sysinfo.traffic_area;
	frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1);
	frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6);
	frame->line_signal = (signal << 8) | (signal << 4) | signal;
}

static void tx_roaming_ident(nmt_t *nmt, frame_t *frame)
{
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Sending identity request.\n");
	if (nmt->tx_frame_count == 8) {
		nmt_release(nmt);
	}
	tx_ident(nmt, frame);
}

static void rx_roaming_ident(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_11a: /* roaming update */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		if (!match_subscriber(nmt, frame))
			break;
		if (nmt->rx_frame_count < 2) {
			PDEBUG(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n");
			break;
		}
		nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3);
		PDEBUG(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", nmt->subscriber.password);
		nmt_new_state(nmt, STATE_ROAMING_CONFIRM);
		nmt->tx_frame_count = 0;
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_roaming_confirm(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 3);
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Send 'ready to receive'.\n");
	if (nmt->tx_frame_count == 2)
		nmt_release(nmt); /* continue with this frame, then release */
}

static void rx_roaming_confirm(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

/*
 * handle call MS -> MTX
 */

static void tx_mo_ident(nmt_t *nmt, frame_t *frame)
{
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Sending identity request.\n");
	if (nmt->tx_frame_count == 8) {
		nmt_release(nmt);
	}
	tx_ident(nmt, frame);
}

static void rx_mo_ident(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_10b: /* seizure */
	case NMT_MESSAGE_12: /* seizure */
		if (!match_channel(nmt, frame))
			break;
		if (!match_subscriber(nmt, frame))
			break;
		if (nmt->rx_frame_count < 2) {
			PDEBUG(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n");
			break;
		}
		nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3);
		PDEBUG(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", nmt->subscriber.password);
		nmt_new_state(nmt, STATE_MO_CONFIRM);
		nmt->tx_frame_count = 0;
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_mo_confirm(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 3);
	if (++nmt->tx_frame_count <= 2) {
		if (nmt->tx_frame_count == 1)
			PDEBUG(DNMT, DEBUG_INFO, "Send 'ready to receive'.\n");
	} else {
		if (nmt->tx_frame_count == 3) {
			PDEBUG(DNMT, DEBUG_INFO, "Send dial tone.\n");
			nmt_new_state(nmt, STATE_MO_DIALING);
			nmt_set_dsp_mode(nmt, DSP_MODE_DIALTONE);
			timer_start(&nmt->timer, DIALING_TO);
		}
	}
}

static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
{
	int len = strlen(nmt->dialing);

	switch (frame->index) {
	case NMT_MESSAGE_14a: /* digits */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		timer_start(&nmt->timer, DIALING_TO);
		/* max digits received */
		if (len + 1 == sizeof(nmt->dialing))
			break;
		/* received odd digit, but be already have odd number of digits */
		if ((len & 1)) {
			if (nmt->rx_frame_count > 1)
				goto missing_digit;
			break;
		} else if (len) {
			if (nmt->rx_frame_count > 3)
				goto missing_digit;
		}
		nmt->dialing[len] = nmt_value2digit(frame->digit);
		nmt->dialing[len + 1] = '\0';
		PDEBUG(DNMT, DEBUG_INFO, "Received (odd)  digit %c.\n", nmt->dialing[len]);
		nmt->rx_frame_count = 0;
		/* finish dial tone after first digit */
		if (!len)
			nmt_set_dsp_mode(nmt, DSP_MODE_AUDIO);
		break;
	case NMT_MESSAGE_14b: /* digits */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		timer_start(&nmt->timer, DIALING_TO);
		/* max digits received */
		if (len + 1 == sizeof(nmt->dialing))
			break;
		/* received odd digit, but be already have odd number of digits */
		if (!(len & 1)) {
			if (len && nmt->rx_frame_count > 1)
				goto missing_digit;
			break;
		} else {
			if (nmt->rx_frame_count > 3)
				goto missing_digit;
		}
		nmt->dialing[len] = nmt_value2digit(frame->digit);
		nmt->dialing[len + 1] = '\0';
		PDEBUG(DNMT, DEBUG_INFO, "Received (even) digit %c.\n", nmt->dialing[len]);
		nmt->rx_frame_count = 0;
		break;
	case NMT_MESSAGE_15: /* idle */
		if (!len)
			break;
		PDEBUG(DNMT, DEBUG_INFO, "Dialing complete %s->%s, call established.\n", &nmt->subscriber.country, nmt->dialing);
		/* setup call */
		{
			int callref = ++new_callref;
			int rc;
			PDEBUG(DNMT, DEBUG_INFO, "Setup call to network.\n");
			rc = call_in_setup(callref, &nmt->subscriber.country, nmt->dialing);
			if (rc < 0) {
				PDEBUG(DNMT, DEBUG_NOTICE, "Call rejected (cause %d), releasing.\n", rc);
				nmt_release(nmt);
				return;
			}
			nmt->sender.callref = callref;
		}
		nmt_new_state(nmt, STATE_MO_COMPLETE);
		nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
		nmt->tx_frame_count = 0;
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}

	return;

missing_digit:
	PDEBUG(DNMT, DEBUG_NOTICE, "Missing digit, aborting.\n");
	nmt_release(nmt);
}

static void tx_mo_complete(nmt_t *nmt, frame_t *frame)
{
	if (++nmt->tx_frame_count <= 4) {
		set_line_signal(nmt, frame, 6);
		if (nmt->tx_frame_count == 1)
			PDEBUG(DNMT, DEBUG_INFO, "Send 'addess complete'.\n");
	} else {
		if (nmt->compander) {
			set_line_signal(nmt, frame, 5);
			if (nmt->tx_frame_count == 5)
				PDEBUG(DNMT, DEBUG_INFO, "Send 'compander in'.\n");
		} else
			frame->index = NMT_MESSAGE_6;
		if (nmt->tx_frame_count == 9) {
			PDEBUG(DNMT, DEBUG_INFO, "Connect audio.\n");
			nmt_new_state(nmt, STATE_ACTIVE);
			nmt->active_state = ACTIVE_STATE_VOICE;
			nmt_set_dsp_mode(nmt, DSP_MODE_AUDIO);
			if (nmt->supervisory) {
				super_reset(nmt);
				timer_start(&nmt->timer, SUPERVISORY_TO1);
			}
		}
	}
}

static void timeout_mo_dialing(nmt_t *nmt)
{
	PDEBUG(DNMT, DEBUG_NOTICE, "Timeout while receiving digits.\n");
	nmt_release(nmt);
	PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
	call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL);
	nmt->sender.callref = 0;
}

/*
 * handle call MTX -> MS
 */
static void tx_mt_paging(nmt_t *nmt, frame_t *frame)
{
	frame->index = NMT_MESSAGE_2a;
	frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
	frame->traffic_area = nmt->sysinfo.traffic_area;
	frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1);
	frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6);
	frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
	if (++nmt->tx_frame_count == 1) {
		PDEBUG(DNMT, DEBUG_INFO, "Send call to mobile.\n");
	} else
		tx_idle(nmt, frame);
	/* wait some time to get answer. use more than one frame due to delay of audio processing */
	if (nmt->tx_frame_count == 5) {
		/* if we page for different channel, we go idle on timeout */
		if (nmt->page_for_nmt) {
			nmt_go_idle(nmt);
			return;
		}
		PDEBUG(DNMT, DEBUG_NOTICE, "No answer from mobile phone (try %d).\n", nmt->page_try);
		if (nmt->page_try == PAGE_TRIES) {
			PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
			call_in_release(nmt->sender.callref, CAUSE_OUTOFORDER);
			nmt->sender.callref = 0;
			nmt_go_idle(nmt);
			return;
		}
		nmt_page(nmt, nmt->subscriber.country, nmt->subscriber.number, nmt->page_try + 1);
	}
}

static void rx_mt_paging(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_10a: /* call acknowledgement */
		if (!match_channel(nmt, frame))
			break;
		if (!match_subscriber(nmt, frame))
			break;
		PDEBUG(DNMT, DEBUG_INFO, "Received call acknowledgement on channel %d.\n", nmt->sender.kanal);
		nmt_new_state(nmt, STATE_MT_CHANNEL);
		nmt->tx_frame_count = 0;
		if (nmt->page_for_nmt) {
			PDEBUG(DNMT, DEBUG_INFO, " -> Notify initiating channel %d about this ack.\n", nmt->mt_channel);
			/* we just send frame 2b on initiating channel, but this is ignored by the phone anyway.
			 * it would be correct to send frame 6 on initiating channel. */
			nmt_new_state(nmt->page_for_nmt, STATE_MT_CHANNEL);
			nmt->page_for_nmt->tx_frame_count = 0;
		}
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_mt_channel(nmt_t *nmt, frame_t *frame)
{
	frame->index = NMT_MESSAGE_2b;
	frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
	frame->traffic_area = nmt->sysinfo.traffic_area;
	frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1);
	frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6);
	frame->tc_no = nmt_encode_channel(nmt->mt_channel, nmt->sysinfo.ms_power);
	PDEBUG(DNMT, DEBUG_INFO, "Send channel activation to mobile.\n");
	/* after assigning for differnet channel, we go idle. */
	if (nmt->page_for_nmt) {
		nmt_go_idle(nmt);
		return;
	}
	nmt_new_state(nmt, STATE_MT_IDENT);
}

static void tx_mt_ident(nmt_t *nmt, frame_t *frame)
{
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Sending identity request.\n");
	if (nmt->tx_frame_count == 8) {
		PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
		call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL);
		nmt->sender.callref = 0;
		nmt_go_idle(nmt);
	}
	tx_ident(nmt, frame);
}

static void rx_mt_ident(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_10b: /* seizure */
		if (!match_subscriber(nmt, frame))
			break;
		nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3);
		PDEBUG(DNMT, DEBUG_INFO, "Received identity (password %s).\n", nmt->subscriber.password);
		nmt_new_state(nmt, STATE_MT_RINGING);
		nmt->tx_frame_count = 0;
		timer_start(&nmt->timer, RINGING_TO);
		call_in_alerting(nmt->sender.callref);
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_mt_ringing(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 9);
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Send 'ringing order'.\n");
	if (nmt->tx_frame_count >= 4)
		frame->index = NMT_MESSAGE_6;
	/* repeat ringing after 5 seconds */
	if (nmt->tx_frame_count == 36)
		nmt->tx_frame_count = 0;
}

static void rx_mt_ringing(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_13a: /* line signal */
		if (!match_subscriber(nmt, frame))
			break;
		if ((frame->line_signal & 0xf) != 14)
			break;
		PDEBUG(DNMT, DEBUG_INFO, "Received 'answer' from phone.\n");
		nmt_new_state(nmt, STATE_MT_COMPLETE);
		nmt->tx_frame_count = 0;
		call_in_answer(nmt->sender.callref, &nmt->subscriber.country);
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_mt_complete(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 5);
	++nmt->tx_frame_count;
	if (nmt->compander) {
		if (nmt->tx_frame_count == 1)
			PDEBUG(DNMT, DEBUG_INFO, "Send 'compander in'.\n");
	} else
		frame->index = NMT_MESSAGE_6;
	if (nmt->tx_frame_count == 5) {
		PDEBUG(DNMT, DEBUG_INFO, "Connect audio.\n");
		nmt_new_state(nmt, STATE_ACTIVE);
		nmt->active_state = ACTIVE_STATE_VOICE;
		nmt_set_dsp_mode(nmt, DSP_MODE_AUDIO);
		if (nmt->supervisory) {
			super_reset(nmt);
			timer_start(&nmt->timer, SUPERVISORY_TO1);
		}
	}
}

static void timeout_mt_ringing(nmt_t *nmt)
{
	PDEBUG(DNMT, DEBUG_NOTICE, "Timeout while waiting for answer of the phone.\n");
	nmt_release(nmt);
	PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
	call_in_release(nmt->sender.callref, CAUSE_NOANSWER);
	nmt->sender.callref = 0;
}

/*
 * handle clearing towards MTX
 */

static void tx_mo_release(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 15);
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Send release.\n");
	if (nmt->tx_frame_count == 4)
		nmt_go_idle(nmt); /* continue with this frame, then go idle */
}

/*
 * handle clearing towards MS
 */

static void tx_mt_release(nmt_t *nmt, frame_t *frame)
{
	set_line_signal(nmt, frame, 15);
	if (++nmt->tx_frame_count == 1)
		PDEBUG(DNMT, DEBUG_INFO, "Send release.\n");
}

static void rx_mt_release(nmt_t *nmt, frame_t *frame)
{
	switch (frame->index) {
	case NMT_MESSAGE_13a: /* line signal */
		if (!match_channel(nmt, frame))
			break;
		if (!match_subscriber(nmt, frame))
			break;
		if ((frame->line_signal & 0xf) != 1)
			break;
		PDEBUG(DNMT, DEBUG_INFO, "Received release guard.\n");
		nmt_go_idle(nmt);
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void timeout_mt_release(nmt_t *nmt)
{
	PDEBUG(DNMT, DEBUG_NOTICE, "Timeout while releasing.\n");
	nmt_go_idle(nmt);
}

/*
 * handle call
 */

void nmt_rx_super(nmt_t *nmt, int tone, double quality)
{
	if (tone)
		PDEBUG(DNMT, DEBUG_INFO, "Detected supervisory signal with quality=%.0f.\n", quality * 100.0);
	else
		PDEBUG(DNMT, DEBUG_INFO, "Lost supervisory signal\n");

	if (nmt->sender.loopback)
		return;

	/* only detect supervisory signal during active call */
	if (nmt->state != STATE_ACTIVE || !nmt->supervisory)
		return;

	if (tone)
		timer_stop(&nmt->timer);
	else
		timer_start(&nmt->timer, SUPERVISORY_TO2);
}

static void timeout_active(nmt_t *nmt, double duration)
{
	if (duration == SUPERVISORY_TO1)
		PDEBUG(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds not receiving supervisory signal.\n", duration);
	else
		PDEBUG(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds loosing supervisory signal.\n", duration);
	nmt_release(nmt);
	PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
	call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL);
	nmt->sender.callref = 0;
}

static void rx_active(nmt_t *nmt, frame_t *frame)
{
	char digit;

	/* restart timer on every reception of frame */
	if (nmt->supervisory)
		timer_start(&nmt->timer, SUPERVISORY_TO2);

	switch (frame->index) {
	case NMT_MESSAGE_13a: /* line signal */
		if (!match_channel(nmt, frame))
			break;
		if (!match_subscriber(nmt, frame))
			break;
		switch ((frame->line_signal & 0xf)) {
		case 5:
			PDEBUG(DNMT, DEBUG_NOTICE, "Register Recall is not supported.\n");
			break;
		case 8:
			if (nmt->active_state != ACTIVE_STATE_VOICE)
				break;
			PDEBUG(DNMT, DEBUG_INFO, "Received 'MFT in' request.\n");
			nmt->active_state = ACTIVE_STATE_MFT_IN;
			nmt->tx_frame_count = 0;
			nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
			nmt->mft_num = 0;
			break;
		case 7:
			if (nmt->active_state != ACTIVE_STATE_MFT)
				break;
			PDEBUG(DNMT, DEBUG_INFO, "Received 'MFT out' request.\n");
			nmt->active_state = ACTIVE_STATE_MFT_OUT;
			nmt->tx_frame_count = 0;
			nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
			break;
		}
		break;
	case NMT_MESSAGE_14a: /* digits */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		if (nmt->active_state != ACTIVE_STATE_MFT)
			break;
		if ((nmt->mft_num & 1))
			break;
		digit = nmt_value2digit(frame->digit);
		dtmf_set_tone(&nmt->dtmf, digit);
		PDEBUG(DNMT, DEBUG_INFO, "Received (odd)  digit %c.\n", digit);
		nmt->mft_num++;
		break;
	case NMT_MESSAGE_14b: /* digits */
		if (!match_channel(nmt, frame))
			break;
		if (!match_area(nmt, frame))
			break;
		if (nmt->active_state != ACTIVE_STATE_MFT)
			break;
		if (!(nmt->mft_num & 1))
			break;
		digit = nmt_value2digit(frame->digit);
		dtmf_set_tone(&nmt->dtmf, digit);
		PDEBUG(DNMT, DEBUG_INFO, "Received (even) digit %c.\n", digit);
		nmt->mft_num++;
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
	}
}

static void tx_active(nmt_t *nmt, frame_t *frame)
{
	switch (nmt->active_state) {
	case ACTIVE_STATE_MFT_IN:
		set_line_signal(nmt, frame, 4);
		if (++nmt->tx_frame_count == 1)
			PDEBUG(DNMT, DEBUG_INFO, "Send 'MFT in acknowledge'.\n");
		if (nmt->tx_frame_count > 4) {
			nmt->active_state = ACTIVE_STATE_MFT;
			nmt_set_dsp_mode(nmt, DSP_MODE_DTMF);
		}
		break;
	case ACTIVE_STATE_MFT_OUT:
		set_line_signal(nmt, frame, 10);
		if (++nmt->tx_frame_count == 1)
			PDEBUG(DNMT, DEBUG_INFO, "Send 'MFT out acknowledge'.\n");
		if (nmt->tx_frame_count > 4) {
			nmt->active_state = ACTIVE_STATE_VOICE;
			nmt_set_dsp_mode(nmt, DSP_MODE_AUDIO);
			if (nmt->supervisory)
				super_reset(nmt);
		}
		break;
	default:
		;
	}
}

/*
 * general handlers to call sub handling
 */

void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed)
{
	frame_t frame;
	int rc;

	PDEBUG(DFRAME, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f\n", level * 100.0 + 0.5, quality * 100.0 + 0.5);

	rc = decode_frame(&frame, bits, (nmt->sender.loopback) ? MTX_TO_XX : XX_TO_MTX, (nmt->state == STATE_MT_PAGING));
	if (rc < 0) {
		PDEBUG(DNMT, (nmt->sender.loopback) ? DEBUG_NOTICE : DEBUG_DEBUG, "Received invalid frame.\n");
		return;
	}

	/* frame counter */
	nmt->rx_frame_count += (int)(frames_elapsed + 0.5);

	PDEBUG(DNMT, (nmt->sender.loopback) ? DEBUG_NOTICE : DEBUG_DEBUG, "Received frame %s\n", nmt_frame_name(frame.index));

	if (nmt->sender.loopback)
		return;

	/* MS releases, but this is not the acknowledge of MTX release */
	if (frame.index == NMT_MESSAGE_13a
	 && (frame.line_signal & 0xf) == 1
	 && nmt->state != STATE_MO_RELEASE
	 && nmt->state != STATE_MT_RELEASE) {
	 	/* drop packets after release */
	 	if (nmt->state == STATE_IDLE)
			return;
		if (!match_subscriber(nmt, &frame))
			return;
		PDEBUG(DNMT, DEBUG_INFO, "Received clearing by mobile phone in state %s.\n", nmt_state_name(nmt->state));
		nmt_new_state(nmt, STATE_MO_RELEASE);
		nmt->tx_frame_count = 0;
		nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
		if (nmt->sender.callref) {
			PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
			call_in_release(nmt->sender.callref, CAUSE_NORMAL);
			nmt->sender.callref = 0;
		}
		return;
	}

	switch (nmt->state) {
	case STATE_IDLE:
		rx_idle(nmt, &frame);
		break;
	case STATE_ROAMING_IDENT:
		rx_roaming_ident(nmt, &frame);
		break;
	case STATE_ROAMING_CONFIRM:
		rx_roaming_confirm(nmt, &frame);
		break;
	case STATE_MO_IDENT:
		rx_mo_ident(nmt, &frame);
		break;
	case STATE_MO_CONFIRM:
	case STATE_MO_DIALING:
		rx_mo_dialing(nmt, &frame);
		break;
	case STATE_MO_RELEASE:
		break;
	case STATE_MT_PAGING:
		rx_mt_paging(nmt, &frame);
		break;
	case STATE_MT_IDENT:
		rx_mt_ident(nmt, &frame);
		break;
	case STATE_MT_RINGING:
		rx_mt_ringing(nmt, &frame);
		break;
	case STATE_MT_RELEASE:
		rx_mt_release(nmt, &frame);
		break;
	case STATE_ACTIVE:
		rx_active(nmt, &frame);
		break;
	default:
		PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame.index), nmt_state_name(nmt->state));
	}
}

/* Timeout handling */
static void nmt_timeout(struct timer *timer)
{
	nmt_t *nmt = (nmt_t *)timer->priv;

	switch (nmt->state) {
	case STATE_MO_DIALING:
		timeout_mo_dialing(nmt);
		break;
	case STATE_MT_RINGING:
		timeout_mt_ringing(nmt);
		break;
	case STATE_MT_RELEASE:
		timeout_mt_release(nmt);
		break;
	case STATE_ACTIVE:
		timeout_active(nmt, timer->duration);
		break;
	default:
		break;
	}
}

/* FSK processing requests next frame after transmission of previous
   frame has been finished. */
const char *nmt_get_frame(nmt_t *nmt)
{
	frame_t frame;
	const char *bits;
	int debug = 1;

	memset(&frame, 0, sizeof(frame));

	switch(nmt->state) {
	case STATE_IDLE:
		tx_idle(nmt, &frame);
		break;
	case STATE_ROAMING_IDENT:
		tx_roaming_ident(nmt, &frame);
		break;
	case STATE_ROAMING_CONFIRM:
		tx_roaming_confirm(nmt, &frame);
		break;
	case STATE_MO_IDENT:
		tx_mo_ident(nmt, &frame);
		break;
	case STATE_MO_CONFIRM:
		tx_mo_confirm(nmt, &frame);
		break;
	case STATE_MO_COMPLETE:
		tx_mo_complete(nmt, &frame);
		break;
	case STATE_MO_RELEASE:
		tx_mo_release(nmt, &frame);
		break;
	case STATE_MT_PAGING:
		tx_mt_paging(nmt, &frame);
		break;
	case STATE_MT_CHANNEL:
		tx_mt_channel(nmt, &frame);
		break;
	case STATE_MT_IDENT:
		tx_mt_ident(nmt, &frame);
		break;
	case STATE_MT_RINGING:
		tx_mt_ringing(nmt, &frame);
		break;
	case STATE_MT_COMPLETE:
		tx_mt_complete(nmt, &frame);
		break;
	case STATE_MT_RELEASE:
		tx_mt_release(nmt, &frame);
		break;
	case STATE_ACTIVE:
		tx_active(nmt, &frame);
		break;
	default:
		break;
	}

	/* no encoding debug for certain (idle) frames */
	switch(frame.index) {
	case NMT_MESSAGE_1a:
	case NMT_MESSAGE_4:
	case NMT_MESSAGE_1b:
	case NMT_MESSAGE_30:
		debug = 0;
		break;
	}

	/* frame sending aborted (e.g. due to audio) */
	if (nmt->dsp_mode != DSP_MODE_FRAME)
		return NULL;

	bits = encode_frame(&frame, debug);

	PDEBUG(DNMT, DEBUG_DEBUG, "Sending frame %s.\n", nmt_frame_name(frame.index));
	return bits;
}

/*
 * call states received from call control
 */

/* Call control starts call towards mobile station. */
int call_out_setup(int callref, char *dialing)
{
	sender_t *sender;
	nmt_t *nmt;
	int i;
	char ms_country;
	char ms_number[7] = "000000";

	/* 1. check if number is invalid, return INVALNUMBER */
	if (dialstring2number(dialing, &ms_country, ms_number)) {
inval:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call to invalid number '%s', rejecting!\n", dialing);
		return -CAUSE_INVALNUMBER;
	}
	for (i = 0; i < 6; i++) {
		if (ms_number[i] < '0' || ms_number[i] > '9')
			goto inval;
	}

	/* 2. check if given number is already in a call, return BUSY */
	for (sender = sender_head; sender; sender = sender->next) {
		nmt = (nmt_t *) sender;
		if (nmt->subscriber.country == ms_country && !strcmp(nmt->subscriber.number, ms_number))
			break;
	}
	if (sender) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call to busy number, rejecting!\n");
		return -CAUSE_BUSY;
	}

	/* 3. check if all senders are busy, return NOCHANNEL */
	for (sender = sender_head; sender; sender = sender->next) {
		nmt = (nmt_t *) sender;
		if (nmt->sysinfo.chan_type != CHAN_TYPE_CC
		 && nmt->sysinfo.chan_type != CHAN_TYPE_CC_TC)
		 	continue;
		if (nmt->state == STATE_IDLE)
			break;
	}
	if (!sender) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call, but no free calling channel, rejecting!\n");
		return -CAUSE_NOCHANNEL;
	}

	PDEBUG(DNMT, DEBUG_INFO, "Call to mobile station, paging station id '%c%s'\n", ms_country, ms_number);

	/* 4. trying to page mobile station */
	sender->callref = callref;
	nmt_page(nmt, ms_country, ms_number, 1);

	return 0;
}

/* Call control sends disconnect (with tones).
 * An active call stays active, so tones and annoucements can be received
 * by mobile station.
 */
void call_out_disconnect(int callref, int cause)
{
	sender_t *sender;
	nmt_t *nmt;

	PDEBUG(DNMT, DEBUG_INFO, "Call has been disconnected by network.\n");

	for (sender = sender_head; sender; sender = sender->next) {
		nmt = (nmt_t *) sender;
		if (sender->callref == callref)
			break;
	}
	if (!sender) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing disconnect, but no callref!\n");
		call_in_release(callref, CAUSE_INVALCALLREF);
		return;
	}

	/* Release when not active */
	if (nmt->state == STATE_ACTIVE)
		return;
	switch (nmt->state) {
	case STATE_MT_RINGING:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing disconnect, during ringing, releasing!\n");
	 	nmt_release(nmt);
		break;
	default:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing disconnect, when phone is in call setup, releasing!\n");
	 	nmt_release(nmt);
		break;
	}

	call_in_release(callref, cause);

	sender->callref = 0;
}

/* Call control releases call toward mobile station. */
void call_out_release(int callref, int cause)
{
	sender_t *sender;
	nmt_t *nmt;

	PDEBUG(DNMT, DEBUG_INFO, "Call has been released by network, releasing call.\n");

	for (sender = sender_head; sender; sender = sender->next) {
		nmt = (nmt_t *) sender;
		if (sender->callref == callref)
			break;
	}
	if (!sender) {
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, but no callref!\n");
		/* don't send release, because caller already released */
		return;
	}

	sender->callref = 0;

	switch (nmt->state) {
	case STATE_ACTIVE:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, during active call, releasing!\n");
	 	nmt_release(nmt);
		break;
	case STATE_MT_RINGING:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, during ringing, releasing!\n");
	 	nmt_release(nmt);
		break;
	default:
		PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, when phone is in call setup, releasing!\n");
	 	nmt_release(nmt);
		break;
	}
}

/* Receive audio from call instance. */
void call_rx_audio(int callref, int16_t *samples, int count)
{
	sender_t *sender;
	nmt_t *nmt;

	for (sender = sender_head; sender; sender = sender->next) {
		nmt = (nmt_t *) sender;
		if (sender->callref == callref)
			break;
	}
	if (!sender)
		return;

	if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
		int16_t up[(int)((double)count * nmt->sender.srstate.factor + 0.5) + 10];
		if (nmt->compander)
			compress_audio(&nmt->cstate, samples, count);
		count = samplerate_upsample(&nmt->sender.srstate, samples, count, up);
		jitter_save(&nmt->sender.audio, up, count);
	}
}

