NMT: Add transaction handling

This commit is contained in:
Andreas Eversberg
2016-08-17 18:25:48 +02:00
parent 82901985af
commit 8cecf13b1a
6 changed files with 425 additions and 215 deletions

View File

@@ -5,6 +5,7 @@ bin_PROGRAMS = \
nmt_SOURCES = \ nmt_SOURCES = \
nmt.c \ nmt.c \
transaction.c \
dsp.c \ dsp.c \
frame.c \ frame.c \
dms.c \ dms.c \

View File

@@ -29,6 +29,7 @@
#include "../common/timer.h" #include "../common/timer.h"
#include "../common/goertzel.h" #include "../common/goertzel.h"
#include "nmt.h" #include "nmt.h"
#include "transaction.h"
#include "dsp.h" #include "dsp.h"
#define PI M_PI #define PI M_PI
@@ -470,7 +471,7 @@ void sender_receive(sender_t *sender, int16_t *samples, int length)
nmt->fsk_filter_pos = pos; nmt->fsk_filter_pos = pos;
if ((nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) if ((nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF)
&& nmt->sender.callref) { && nmt->trans && nmt->trans->callref) {
int16_t down[length]; /* more than enough */ int16_t down[length]; /* more than enough */
int count; int count;
@@ -484,7 +485,7 @@ void sender_receive(sender_t *sender, int16_t *samples, int length)
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
spl[pos++] = down[i]; spl[pos++] = down[i];
if (pos == 160) { if (pos == 160) {
call_tx_audio(nmt->sender.callref, spl, 160); call_tx_audio(nmt->trans->callref, spl, 160);
pos = 0; pos = 0;
} }
} }

View File

@@ -29,18 +29,21 @@
#include "../common/timer.h" #include "../common/timer.h"
#include "../common/cause.h" #include "../common/cause.h"
#include "nmt.h" #include "nmt.h"
#include "transaction.h"
#include "dsp.h" #include "dsp.h"
#include "frame.h" #include "frame.h"
/* How does paging on all channels work: /* How does paging on all channels work:
* *
* Paging is performed on the channel that is associated with the call and * Paging is performed on all free CC channels. The transaction pointer
* on all other free calling channels. * is set towards transaction. (nmt->trans points to trans)
*
* After timeout, all channels with that transaction pointer are released.
*
* When paging was replied, other channels with the transaction pointer are
* relased, but the channel with the reply is linked to transaction in both
* directions. (trans->nmt points to nmt, nmt->trans points to trans)
* *
* All other calling channels have a pointer "page_for_nmt" that points to
* the transceiver instance with the call. During paging all calling channels
* are busy, so no call can be setup. After paging is complete, the other
* calling channels return idle.
*/ */
static int sms_ref = 0; static int sms_ref = 0;
@@ -50,6 +53,7 @@ static int sms_ref = 0;
static int new_callref = 0x40000000; static int new_callref = 0x40000000;
/* Timers */ /* Timers */
#define PAGING_TO 1.0 /* wait for paging response: fictive value */
#define RELEASE_TO 2.0 /* how long do we wait for release guard of the phone */ #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 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 CHANNEL_TO 2.0 /* how long do we wait for phone to appear on assigned channel */
@@ -297,7 +301,6 @@ uint8_t nmt_country_by_short_name(const char *short_name)
} }
static void nmt_timeout(struct timer *timer); static void nmt_timeout(struct timer *timer);
static void nmt_go_idle(nmt_t *nmt);
/* Create transceiver instance and link to a list. */ /* 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 compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback) 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 compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback)
@@ -423,18 +426,17 @@ void nmt_destroy(sender_t *sender)
} }
/* Abort connection towards mobile station by sending idle digits. */ /* Abort connection towards mobile station by sending idle digits. */
static void nmt_go_idle(nmt_t *nmt) void nmt_go_idle(nmt_t *nmt)
{ {
timer_stop(&nmt->timer); timer_stop(&nmt->timer);
nmt->page_for_nmt = NULL;
nmt->dms_call = 0; nmt->dms_call = 0;
dms_reset(nmt); dms_reset(nmt);
sms_reset(nmt); sms_reset(nmt);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type)); PDEBUG_CHAN(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_new_state(nmt, STATE_IDLE);
nmt->trans = NULL;
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME); nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
memset(&nmt->subscriber, 0, sizeof(nmt->subscriber));
memset(&nmt->dialing, 0, sizeof(nmt->dialing)); memset(&nmt->dialing, 0, sizeof(nmt->dialing));
#if 0 #if 0
@@ -448,9 +450,15 @@ static void nmt_go_idle(nmt_t *nmt)
/* release an ongoing connection, this is used by roaming update and release initiated by MTX */ /* release an ongoing connection, this is used by roaming update and release initiated by MTX */
static void nmt_release(nmt_t *nmt) static void nmt_release(nmt_t *nmt)
{ {
transaction_t *trans = nmt->trans;
timer_stop(&nmt->timer); timer_stop(&nmt->timer);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Releasing connection towards mobile station.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Releasing connection towards mobile station.\n");
if (trans->callref) {
PDEBUG_CHAN(DNMT, DEBUG_ERROR, "Callref already set, please fix!\n");
abort();
}
nmt_new_state(nmt, STATE_MT_RELEASE); nmt_new_state(nmt, STATE_MT_RELEASE);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME); nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
@@ -458,35 +466,27 @@ static void nmt_release(nmt_t *nmt)
} }
/* Enter paging state and transmit phone's number on calling channel */ /* 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) static void nmt_page(transaction_t *trans, int try)
{ {
sender_t *sender; sender_t *sender;
nmt_t *other; nmt_t *nmt;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Entering paging state (try %d), sending call to '%c,%s'.\n", try, ms_country, ms_number); PDEBUG(DNMT, DEBUG_INFO, "Entering paging state (try %d), sending call to '%c,%s'.\n", try, trans->subscriber.country, trans->subscriber.number);
nmt->subscriber.country = ms_country; trans->page_try = try;
strcpy(nmt->subscriber.number, ms_number); timer_start(&trans->timer, PAGING_TO);
nmt->page_try = try;
/* page on all CC (CC/TC) */ /* page on all CC (CC/TC) */
for (sender = sender_head; sender; sender = sender->next) { for (sender = sender_head; sender; sender = sender->next) {
other = (nmt_t *)sender; nmt = (nmt_t *)sender;
if (other == nmt) { if (nmt->sysinfo.chan_type != CHAN_TYPE_CC
/* this is us */ && nmt->sysinfo.chan_type != CHAN_TYPE_CC_TC)
PDEBUG(DNMT, DEBUG_INFO, "Paging on our channel %d.\n", other->sender.kanal); continue;
} else { if (nmt->state != STATE_IDLE)
/* this is not us */ continue;
if (other->sysinfo.chan_type != CHAN_TYPE_CC PDEBUG(DNMT, DEBUG_INFO, "Paging on channel %d.\n", sender->kanal);
&& other->sysinfo.chan_type != CHAN_TYPE_CC_TC) nmt_new_state(nmt, STATE_MT_PAGING);
continue; nmt->trans = trans;
if (other->state != STATE_IDLE) nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
continue; nmt->tx_frame_count = 0;
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->...) */
} }
} }
@@ -536,14 +536,14 @@ skip_area:
} }
/* check match subscriber number */ /* check match subscriber number */
static int match_subscriber(nmt_t *nmt, frame_t *frame) static int match_subscriber(transaction_t *trans, frame_t *frame)
{ {
if (nmt_digits2value(&nmt->subscriber.country, 1) != frame->ms_country) { if (nmt_digits2value(&trans->subscriber.country, 1) != frame->ms_country) {
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Received non matching subscriber counrtry, ignoring.\n"); PDEBUG(DNMT, DEBUG_NOTICE, "Received non matching subscriber counrtry, ignoring.\n");
return 0; return 0;
} }
if (nmt_digits2value(nmt->subscriber.number, 6) != frame->ms_number) { if (nmt_digits2value(trans->subscriber.number, 6) != frame->ms_number) {
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Received non matching subscriber number, ignoring.\n"); PDEBUG(DNMT, DEBUG_NOTICE, "Received non matching subscriber number, ignoring.\n");
return 0; return 0;
} }
@@ -556,21 +556,25 @@ static int match_subscriber(nmt_t *nmt, frame_t *frame)
static void tx_ident(nmt_t *nmt, frame_t *frame) static void tx_ident(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_3b; frame->mt = NMT_MESSAGE_3b;
frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power); frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
frame->traffic_area = nmt->sysinfo.traffic_area; frame->traffic_area = nmt->sysinfo.traffic_area;
frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1); frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6); frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no); 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) static void set_line_signal(nmt_t *nmt, frame_t *frame, uint8_t signal)
{ {
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_5a; frame->mt = NMT_MESSAGE_5a;
frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power); frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
frame->traffic_area = nmt->sysinfo.traffic_area; frame->traffic_area = nmt->sysinfo.traffic_area;
frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1); frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6); frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->line_signal = (signal << 8) | (signal << 4) | signal; frame->line_signal = (signal << 8) | (signal << 4) | signal;
} }
@@ -681,6 +685,9 @@ static void tx_idle(nmt_t *nmt, frame_t *frame)
static void rx_idle(nmt_t *nmt, frame_t *frame) static void rx_idle(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans;
nmt_subscriber_t subscr;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_11a: /* roaming update and seizure */ case NMT_MESSAGE_11a: /* roaming update and seizure */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
@@ -688,13 +695,20 @@ static void rx_idle(nmt_t *nmt, frame_t *frame)
if (!match_area(nmt, frame)) if (!match_area(nmt, frame))
break; break;
/* set subscriber */ /* set subscriber */
nmt_value2digits(frame->ms_country, &nmt->subscriber.country, 1); memset(&subscr, 0, sizeof(subscr));
nmt_value2digits(frame->ms_number, nmt->subscriber.number, 6); nmt_value2digits(frame->ms_country, &subscr.country, 1);
nmt->subscriber.number[6] = '\0'; nmt_value2digits(frame->ms_number, subscr.number, 6);
trans = create_transaction(&subscr);
if (!trans) {
PDEBUG(DNMT, DEBUG_NOTICE, "Failed to create transaction!\n");
break;
}
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received roaming seizure from subscriber %c,%s\n", nmt->subscriber.country, nmt->subscriber.number); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received roaming seizure from subscriber %c,%s\n", subscr.country, subscr.number);
/* change state */ /* change state */
nmt_new_state(nmt, STATE_ROAMING_IDENT); nmt_new_state(nmt, STATE_ROAMING_IDENT);
nmt->trans = trans;
trans->nmt = nmt;
nmt->rx_frame_count = 0; nmt->rx_frame_count = 0;
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
break; break;
@@ -705,15 +719,22 @@ static void rx_idle(nmt_t *nmt, frame_t *frame)
if (!match_area(nmt, frame)) if (!match_area(nmt, frame))
break; break;
/* set subscriber */ /* set subscriber */
nmt_value2digits(frame->ms_country, &nmt->subscriber.country, 1); memset(&subscr, 0, sizeof(subscr));
nmt_value2digits(frame->ms_number, nmt->subscriber.number, 6); nmt_value2digits(frame->ms_country, &subscr.country, 1);
nmt_value2digits(frame->ms_number, subscr.number, 6);
if (frame->mt == NMT_MESSAGE_12) if (frame->mt == NMT_MESSAGE_12)
nmt->subscriber.coinbox = 1; subscr.coinbox = 1;
nmt->subscriber.number[6] = '\0'; trans = create_transaction(&subscr);
if (!trans) {
PDEBUG(DNMT, DEBUG_NOTICE, "Failed to create transaction!\n");
break;
}
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received call from subscriber %c,%s%s\n", nmt->subscriber.country, nmt->subscriber.number, (nmt->subscriber.coinbox) ? " (coinbox)" : ""); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received call from subscriber %c,%s%s\n", subscr.country, subscr.number, (subscr.coinbox) ? " (coinbox)" : "");
/* change state */ /* change state */
nmt_new_state(nmt, STATE_MO_IDENT); nmt_new_state(nmt, STATE_MO_IDENT);
nmt->trans = trans;
trans->nmt = nmt;
nmt->rx_frame_count = 0; nmt->rx_frame_count = 0;
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
break; break;
@@ -741,20 +762,22 @@ static void tx_roaming_ident(nmt_t *nmt, frame_t *frame)
static void rx_roaming_ident(nmt_t *nmt, frame_t *frame) static void rx_roaming_ident(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_11a: /* roaming update */ case NMT_MESSAGE_11a: /* roaming update */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_area(nmt, frame)) if (!match_area(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
if (nmt->rx_frame_count < 2) { if (nmt->rx_frame_count < 2) {
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n"); PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n");
break; break;
} }
nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3); nmt_value2digits(frame->ms_password, trans->subscriber.password, 3);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", nmt->subscriber.password); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", trans->subscriber.password);
nmt_new_state(nmt, STATE_ROAMING_CONFIRM); nmt_new_state(nmt, STATE_ROAMING_CONFIRM);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
break; break;
@@ -796,19 +819,21 @@ static void tx_mo_ident(nmt_t *nmt, frame_t *frame)
static void rx_mo_ident(nmt_t *nmt, frame_t *frame) static void rx_mo_ident(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_10b: /* seizure */ case NMT_MESSAGE_10b: /* seizure */
case NMT_MESSAGE_12: /* seizure */ case NMT_MESSAGE_12: /* seizure */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
if (nmt->rx_frame_count < 2) { if (nmt->rx_frame_count < 2) {
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n"); PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Skipping second seizure frame\n");
break; break;
} }
nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3); nmt_value2digits(frame->ms_password, trans->subscriber.password, 3);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", nmt->subscriber.password); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity confirm (password %s).\n", trans->subscriber.password);
nmt_new_state(nmt, STATE_MO_CONFIRM); nmt_new_state(nmt, STATE_MO_CONFIRM);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
break; break;
@@ -836,6 +861,7 @@ static void tx_mo_confirm(nmt_t *nmt, frame_t *frame)
static void rx_mo_dialing(nmt_t *nmt, frame_t *frame) static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
{ {
int len = strlen(nmt->dialing); int len = strlen(nmt->dialing);
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_14a: /* digits */ case NMT_MESSAGE_14a: /* digits */
@@ -843,6 +869,8 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
break; break;
if (!match_area(nmt, frame)) if (!match_area(nmt, frame))
break; break;
if (!match_subscriber(trans, frame))
break;
timer_start(&nmt->timer, DIALING_TO); timer_start(&nmt->timer, DIALING_TO);
/* max digits received */ /* max digits received */
if (len + 1 == sizeof(nmt->dialing)) if (len + 1 == sizeof(nmt->dialing))
@@ -869,6 +897,8 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
break; break;
if (!match_area(nmt, frame)) if (!match_area(nmt, frame))
break; break;
if (!match_subscriber(trans, frame))
break;
timer_start(&nmt->timer, DIALING_TO); timer_start(&nmt->timer, DIALING_TO);
/* max digits received */ /* max digits received */
if (len + 1 == sizeof(nmt->dialing)) if (len + 1 == sizeof(nmt->dialing))
@@ -890,7 +920,7 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
case NMT_MESSAGE_15: /* idle */ case NMT_MESSAGE_15: /* idle */
if (!len) if (!len)
break; break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Dialing complete %s->%s, call established.\n", &nmt->subscriber.country, nmt->dialing); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Dialing complete %s->%s, call established.\n", &trans->subscriber.country, nmt->dialing);
/* setup call */ /* setup call */
if (!strcmp(nmt->dialing, nmt->smsc_number)) { if (!strcmp(nmt->dialing, nmt->smsc_number)) {
/* SMS */ /* SMS */
@@ -900,14 +930,15 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
int callref = ++new_callref; int callref = ++new_callref;
int rc; int rc;
PDEBUG(DNMT, DEBUG_INFO, "Setup call to network.\n"); PDEBUG(DNMT, DEBUG_INFO, "Setup call to network.\n");
rc = call_in_setup(callref, &nmt->subscriber.country, nmt->dialing); rc = call_in_setup(callref, &trans->subscriber.country, nmt->dialing);
if (rc < 0) { if (rc < 0) {
PDEBUG(DNMT, DEBUG_NOTICE, "Call rejected (cause %d), releasing.\n", rc); PDEBUG(DNMT, DEBUG_NOTICE, "Call rejected (cause %d), releasing.\n", rc);
nmt_release(nmt); nmt_release(nmt);
return; return;
} }
nmt->sender.callref = callref; trans->callref = callref;
} }
timer_stop(&nmt->timer);
nmt_new_state(nmt, STATE_MO_COMPLETE); nmt_new_state(nmt, STATE_MO_COMPLETE);
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME); nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
@@ -954,8 +985,6 @@ static void timeout_mo_dialing(nmt_t *nmt)
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while receiving digits.\n"); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while receiving digits.\n");
nmt_release(nmt); nmt_release(nmt);
PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n"); PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL);
nmt->sender.callref = 0;
} }
/* /*
@@ -963,64 +992,58 @@ static void timeout_mo_dialing(nmt_t *nmt)
*/ */
static void tx_mt_paging(nmt_t *nmt, frame_t *frame) static void tx_mt_paging(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_2a; frame->mt = NMT_MESSAGE_2a;
frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power); frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
frame->traffic_area = nmt->sysinfo.traffic_area; frame->traffic_area = nmt->sysinfo.traffic_area;
if (nmt->page_for_nmt) { frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_country = nmt_digits2value(&nmt->page_for_nmt->subscriber.country, 1); frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->ms_number = nmt_digits2value(nmt->page_for_nmt->subscriber.number, 6);
} else {
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); frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
if (++nmt->tx_frame_count == 1) { if (++nmt->tx_frame_count == 1) {
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send call to mobile.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send call to mobile.\n");
} else } else
tx_idle(nmt, frame); 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 */ void timeout_mt_paging(transaction_t *trans)
if (nmt->page_for_nmt) { {
nmt_go_idle(nmt); PDEBUG(DNMT, DEBUG_NOTICE, "No answer from mobile phone (try %d).\n", trans->page_try);
return; if (trans->page_try == PAGE_TRIES) {
} PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "No answer from mobile phone (try %d).\n", nmt->page_try); call_in_release(trans->callref, CAUSE_OUTOFORDER);
if (nmt->page_try == PAGE_TRIES) { destroy_transaction(trans);
PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n"); return;
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);
} }
nmt_page(trans, trans->page_try + 1);
} }
static void rx_mt_paging(nmt_t *nmt, frame_t *frame) static void rx_mt_paging(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
sender_t *sender;
nmt_t *other;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_10a: /* call acknowledgment */ case NMT_MESSAGE_10a: /* call acknowledgment */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (nmt->page_for_nmt) { if (!match_subscriber(trans, frame))
if (!match_subscriber(nmt->page_for_nmt, frame)) break;
break; PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received call acknowledgment from subscriber %c,%s.\n", trans->subscriber.country, trans->subscriber.number);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received call acknowledgment from subscriber %c,%s.\n", nmt->page_for_nmt->subscriber.country, nmt->page_for_nmt->subscriber.number); if (trans->sms_string[0])
PDEBUG(DNMT, DEBUG_INFO, " -> Notify initiating channel %d about this ack.\n", nmt->mt_channel); nmt->dms_call = 1;
/* we just send frame 2b on initiating channel, but this is ignored by the phone anyway. timer_stop(&trans->timer);
* it would be correct to send frame 6 on initiating channel. */ nmt_new_state(nmt, STATE_MT_CHANNEL);
nmt_new_state(nmt->page_for_nmt, STATE_MT_CHANNEL); trans->nmt = nmt;
nmt->page_for_nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
/* also send frame 2b on this channel to order phone to initiating channel */ /* release other channels */
nmt_new_state(nmt, STATE_MT_CHANNEL); for (sender = sender_head; sender; sender = sender->next) {
nmt->tx_frame_count = 0; other = (nmt_t *)sender;
} else { if (other == nmt)
if (!match_subscriber(nmt, frame)) continue;
break; if (other->trans == trans)
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received call acknowledgment from subscriber %c,%s.\n", nmt->subscriber.country, nmt->subscriber.number); nmt_go_idle(other);
nmt_new_state(nmt, STATE_MT_CHANNEL);
nmt->tx_frame_count = 0;
} }
break; break;
default: default:
@@ -1030,47 +1053,42 @@ static void rx_mt_paging(nmt_t *nmt, frame_t *frame)
static void tx_mt_channel(nmt_t *nmt, frame_t *frame) static void tx_mt_channel(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_2b; frame->mt = NMT_MESSAGE_2b;
frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power); frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
frame->traffic_area = nmt->sysinfo.traffic_area; frame->traffic_area = nmt->sysinfo.traffic_area;
if (nmt->page_for_nmt) { frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_country = nmt_digits2value(&nmt->page_for_nmt->subscriber.country, 1); frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->ms_number = nmt_digits2value(nmt->page_for_nmt->subscriber.number, 6); frame->tc_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
} else {
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_CHAN(DNMT, DEBUG_INFO, "Send channel activation to mobile.\n"); PDEBUG_CHAN(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); nmt_new_state(nmt, STATE_MT_IDENT);
} }
static void tx_mt_ident(nmt_t *nmt, frame_t *frame) static void tx_mt_ident(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
if (++nmt->tx_frame_count == 1) if (++nmt->tx_frame_count == 1)
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Sending identity request.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Sending identity request.\n");
if (nmt->tx_frame_count == 8) { if (nmt->tx_frame_count == 8) {
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Release call towards network.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Release call towards network.\n");
call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL); call_in_release(trans->callref, CAUSE_TEMPFAIL);
nmt->sender.callref = 0; destroy_transaction(trans);
nmt_go_idle(nmt);
} }
tx_ident(nmt, frame); tx_ident(nmt, frame);
} }
static void rx_mt_ident(nmt_t *nmt, frame_t *frame) static void rx_mt_ident(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_10b: /* seizure */ case NMT_MESSAGE_10b: /* seizure */
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
nmt_value2digits(frame->ms_password, nmt->subscriber.password, 3); nmt_value2digits(frame->ms_password, trans->subscriber.password, 3);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity (password %s).\n", nmt->subscriber.password); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received identity (password %s).\n", trans->subscriber.password);
if (nmt->dms_call) { if (nmt->dms_call) {
nmt_new_state(nmt, STATE_MT_AUTOANSWER); nmt_new_state(nmt, STATE_MT_AUTOANSWER);
nmt->wait_autoanswer = 1; nmt->wait_autoanswer = 1;
@@ -1086,7 +1104,7 @@ static void rx_mt_ident(nmt_t *nmt, frame_t *frame)
nmt->tx_callerid_count = 0; nmt->tx_callerid_count = 0;
} }
timer_start(&nmt->timer, RINGING_TO); timer_start(&nmt->timer, RINGING_TO);
call_in_alerting(nmt->sender.callref); call_in_alerting(trans->callref);
} }
break; break;
default: default:
@@ -1096,6 +1114,8 @@ static void rx_mt_ident(nmt_t *nmt, frame_t *frame)
static void tx_mt_autoanswer(nmt_t *nmt, frame_t *frame) static void tx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
/* first we need to wait for autoanswer */ /* first we need to wait for autoanswer */
if (nmt->wait_autoanswer) { if (nmt->wait_autoanswer) {
frame->mt = NMT_MESSAGE_6; frame->mt = NMT_MESSAGE_6;
@@ -1110,12 +1130,14 @@ static void tx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
nmt->tx_callerid_count = 0; nmt->tx_callerid_count = 0;
timer_start(&nmt->timer, RINGING_TO); timer_start(&nmt->timer, RINGING_TO);
call_in_alerting(nmt->sender.callref); call_in_alerting(trans->callref);
} }
} }
static void rx_mt_autoanswer(nmt_t *nmt, frame_t *frame) static void rx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_15: /* idle */ case NMT_MESSAGE_15: /* idle */
nmt->wait_autoanswer = 0; nmt->wait_autoanswer = 0;
@@ -1123,14 +1145,14 @@ static void rx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
case NMT_MESSAGE_13a: /* line signal */ case NMT_MESSAGE_13a: /* line signal */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
if ((frame->line_signal & 0xf) != 12) if ((frame->line_signal & 0xf) != 12)
break; break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received acknowledge to autoanswer.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received acknowledge to autoanswer.\n");
nmt_new_state(nmt, STATE_MT_COMPLETE); nmt_new_state(nmt, STATE_MT_COMPLETE);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
call_in_answer(nmt->sender.callref, &nmt->subscriber.country); call_in_answer(trans->callref, &trans->subscriber.country);
break; break;
default: default:
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state)); PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state));
@@ -1139,6 +1161,8 @@ static void rx_mt_autoanswer(nmt_t *nmt, frame_t *frame)
static void tx_mt_ringing(nmt_t *nmt, frame_t *frame) static void tx_mt_ringing(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
set_line_signal(nmt, frame, 9); set_line_signal(nmt, frame, 9);
if (++nmt->tx_frame_count == 1) if (++nmt->tx_frame_count == 1)
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send 'ringing order'.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send 'ringing order'.\n");
@@ -1146,7 +1170,7 @@ static void tx_mt_ringing(nmt_t *nmt, frame_t *frame)
if (nmt->tx_callerid_count) { if (nmt->tx_callerid_count) {
if (nmt->tx_frame_count == 5) if (nmt->tx_frame_count == 5)
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send 'A-number'.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send 'A-number'.\n");
encode_a_number(nmt, frame, nmt->tx_frame_count - 4, nmt->caller_type, nmt->caller_id); encode_a_number(nmt, frame, nmt->tx_frame_count - 4, trans->caller_type, trans->caller_id);
} else } else
frame->mt = NMT_MESSAGE_6; frame->mt = NMT_MESSAGE_6;
} }
@@ -1166,11 +1190,13 @@ static void tx_mt_ringing(nmt_t *nmt, frame_t *frame)
static void rx_mt_ringing(nmt_t *nmt, frame_t *frame) static void rx_mt_ringing(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_13a: /* line signal */ case NMT_MESSAGE_13a: /* line signal */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
if ((frame->line_signal & 0xf) != 14) if ((frame->line_signal & 0xf) != 14)
break; break;
@@ -1178,7 +1204,7 @@ static void rx_mt_ringing(nmt_t *nmt, frame_t *frame)
nmt_new_state(nmt, STATE_MT_COMPLETE); nmt_new_state(nmt, STATE_MT_COMPLETE);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
timer_stop(&nmt->timer); timer_stop(&nmt->timer);
call_in_answer(nmt->sender.callref, &nmt->subscriber.country); call_in_answer(trans->callref, &trans->subscriber.country);
break; break;
default: default:
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state)); PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state));
@@ -1187,6 +1213,8 @@ static void rx_mt_ringing(nmt_t *nmt, frame_t *frame)
static void tx_mt_complete(nmt_t *nmt, frame_t *frame) static void tx_mt_complete(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
++nmt->tx_frame_count; ++nmt->tx_frame_count;
if (nmt->compandor && !nmt->dms_call) { if (nmt->compandor && !nmt->dms_call) {
if (nmt->tx_frame_count == 1) if (nmt->tx_frame_count == 1)
@@ -1205,18 +1233,20 @@ static void tx_mt_complete(nmt_t *nmt, frame_t *frame)
} }
if (nmt->dms_call) { if (nmt->dms_call) {
time_t ti = time(NULL); time_t ti = time(NULL);
sms_deliver(nmt, sms_ref, nmt->caller_id, nmt->caller_type, SMS_PLAN_ISDN_TEL, ti, nmt->sms_string); sms_deliver(nmt, sms_ref, trans->caller_id, trans->caller_type, SMS_PLAN_ISDN_TEL, ti, trans->sms_string);
} }
} }
} }
static void timeout_mt_ringing(nmt_t *nmt) static void timeout_mt_ringing(nmt_t *nmt)
{ {
transaction_t *trans = nmt->trans;
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while waiting for answer of the phone.\n"); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while waiting for answer of the phone.\n");
nmt_release(nmt);
PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n"); PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
call_in_release(nmt->sender.callref, CAUSE_NOANSWER); call_in_release(trans->callref, CAUSE_NOANSWER);
nmt->sender.callref = 0; trans->callref = 0;
nmt_release(nmt);
} }
/* /*
@@ -1225,11 +1255,13 @@ static void timeout_mt_ringing(nmt_t *nmt)
static void tx_mo_release(nmt_t *nmt, frame_t *frame) static void tx_mo_release(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
set_line_signal(nmt, frame, 15); set_line_signal(nmt, frame, 15);
if (++nmt->tx_frame_count == 1) if (++nmt->tx_frame_count == 1)
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send release.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send release.\n");
if (nmt->tx_frame_count == 4) if (nmt->tx_frame_count == 4)
nmt_go_idle(nmt); /* continue with this frame, then go idle */ destroy_transaction(trans); /* continue with this frame, then go idle */
} }
/* /*
@@ -1245,16 +1277,19 @@ static void tx_mt_release(nmt_t *nmt, frame_t *frame)
static void rx_mt_release(nmt_t *nmt, frame_t *frame) static void rx_mt_release(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
switch (frame->mt) { switch (frame->mt) {
case NMT_MESSAGE_13a: /* line signal */ case NMT_MESSAGE_13a: /* line signal */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
if ((frame->line_signal & 0xf) != 1) if ((frame->line_signal & 0xf) != 1)
break; break;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received release guard.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received release guard.\n");
nmt_go_idle(nmt); timer_stop(&nmt->timer);
destroy_transaction(trans);
break; break;
default: default:
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state)); PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->mt), nmt_state_name(nmt->state));
@@ -1263,8 +1298,10 @@ static void rx_mt_release(nmt_t *nmt, frame_t *frame)
static void timeout_mt_release(nmt_t *nmt) static void timeout_mt_release(nmt_t *nmt)
{ {
transaction_t *trans = nmt->trans;
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while releasing.\n"); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout while releasing.\n");
nmt_go_idle(nmt); destroy_transaction(trans);
} }
/* /*
@@ -1293,18 +1330,21 @@ void nmt_rx_super(nmt_t *nmt, int tone, double quality)
static void timeout_active(nmt_t *nmt, double duration) static void timeout_active(nmt_t *nmt, double duration)
{ {
transaction_t *trans = nmt->trans;
if (duration == SUPERVISORY_TO1) if (duration == SUPERVISORY_TO1)
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds not receiving supervisory signal.\n", duration); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds not receiving supervisory signal.\n", duration);
else else
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds loosing supervisory signal.\n", duration); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Timeout after %.0f seconds loosing supervisory signal.\n", duration);
nmt_release(nmt);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Release call towards network.\n"); PDEBUG_CHAN(DNMT, DEBUG_INFO, "Release call towards network.\n");
call_in_release(nmt->sender.callref, CAUSE_TEMPFAIL); call_in_release(trans->callref, CAUSE_TEMPFAIL);
nmt->sender.callref = 0; trans->callref = 0;
nmt_release(nmt);
} }
static void rx_active(nmt_t *nmt, frame_t *frame) static void rx_active(nmt_t *nmt, frame_t *frame)
{ {
transaction_t *trans = nmt->trans;
char digit; char digit;
/* restart timer on every reception of frame */ /* restart timer on every reception of frame */
@@ -1315,7 +1355,7 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
case NMT_MESSAGE_13a: /* line signal */ case NMT_MESSAGE_13a: /* line signal */
if (!match_channel(nmt, frame)) if (!match_channel(nmt, frame))
break; break;
if (!match_subscriber(nmt, frame)) if (!match_subscriber(trans, frame))
break; break;
switch ((frame->line_signal & 0xf)) { switch ((frame->line_signal & 0xf)) {
case 5: case 5:
@@ -1434,16 +1474,16 @@ void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double leve
/* drop packets after release */ /* drop packets after release */
if (nmt->state == STATE_IDLE) if (nmt->state == STATE_IDLE)
return; return;
if (!match_subscriber(nmt, &frame)) if (!match_subscriber(nmt->trans, &frame))
return; return;
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received clearing by mobile phone in state %s.\n", nmt_state_name(nmt->state)); PDEBUG_CHAN(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_new_state(nmt, STATE_MO_RELEASE);
nmt->tx_frame_count = 0; nmt->tx_frame_count = 0;
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME); nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
if (nmt->sender.callref) { if (nmt->trans->callref) {
PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n"); PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
call_in_release(nmt->sender.callref, CAUSE_NORMAL); call_in_release(nmt->trans->callref, CAUSE_NORMAL);
nmt->sender.callref = 0; nmt->trans->callref = 0;
} }
return; return;
} }
@@ -1605,32 +1645,30 @@ int _out_setup(int callref, const char *caller_id, enum number_type caller_type,
sender_t *sender; sender_t *sender;
nmt_t *nmt; nmt_t *nmt;
int i; int i;
char ms_country; nmt_subscriber_t subscr;
char ms_number[7] = "000000"; transaction_t *trans;
memset(&subscr, 0, sizeof(subscr));
/* 1. check if number is invalid, return INVALNUMBER */ /* 1. check if number is invalid, return INVALNUMBER */
if (dialstring2number(dialing, &ms_country, ms_number)) { if (dialstring2number(dialing, &subscr.country, subscr.number)) {
inval: inval:
PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call to invalid number '%s', rejecting!\n", dialing); PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call to invalid number '%s', rejecting!\n", dialing);
return -CAUSE_INVALNUMBER; return -CAUSE_INVALNUMBER;
} }
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
if (ms_number[i] < '0' || ms_number[i] > '9') if (subscr.number[i] < '0' || subscr.number[i] > '9')
goto inval; goto inval;
} }
/* 2. check if given number is already in a call, return BUSY */ /* 2. check if given number is already in a call, return BUSY */
for (sender = sender_head; sender; sender = sender->next) { trans = get_transaction_by_number(&subscr);
nmt = (nmt_t *) sender; if (trans) {
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"); PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing call to busy number, rejecting!\n");
return -CAUSE_BUSY; return -CAUSE_BUSY;
} }
/* 3. check if all senders are busy, return NOCHANNEL */ /* 3. check if all paging (calling) channels are busy, return NOCHANNEL */
for (sender = sender_head; sender; sender = sender->next) { for (sender = sender_head; sender; sender = sender->next) {
nmt = (nmt_t *) sender; nmt = (nmt_t *) sender;
if (nmt->sysinfo.chan_type != CHAN_TYPE_CC if (nmt->sysinfo.chan_type != CHAN_TYPE_CC
@@ -1644,21 +1682,25 @@ inval:
return -CAUSE_NOCHANNEL; return -CAUSE_NOCHANNEL;
} }
PDEBUG(DNMT, DEBUG_INFO, "Call to mobile station, paging station id '%c%s'\n", ms_country, ms_number); PDEBUG(DNMT, DEBUG_INFO, "Call to mobile station, paging station id '%c%s'\n", subscr.country, subscr.number);
/* 4. trying to page mobile station */ /* 4. trying to page mobile station */
sender->callref = callref; trans = create_transaction(&subscr);
if (!trans) {
PDEBUG(DNMT, DEBUG_NOTICE, "Failed to create transaction, rejecting!\n");
return -CAUSE_TEMPFAIL;
}
trans->callref = callref;
if (sms) { if (sms) {
strncpy(nmt->sms_string, sms, sizeof(nmt->sms_string) - 1); strncpy(trans->sms_string, sms, sizeof(trans->sms_string) - 1);
nmt->dms_call = 1;
} }
if (caller_type == TYPE_INTERNATIONAL) { if (caller_type == TYPE_INTERNATIONAL) {
nmt->caller_id[0] = '+'; /* not done by phone */ trans->caller_id[0] = '+'; /* not done by phone */
strncpy(nmt->caller_id + 1, caller_id, sizeof(nmt->caller_id) - 2); strncpy(trans->caller_id + 1, caller_id, sizeof(trans->caller_id) - 2);
} else } else
strncpy(nmt->caller_id, caller_id, sizeof(nmt->caller_id) - 1); strncpy(trans->caller_id, caller_id, sizeof(trans->caller_id) - 1);
nmt->caller_type = caller_type; trans->caller_type = caller_type;
nmt_page(nmt, ms_country, ms_number, 1); nmt_page(trans, 1);
return 0; return 0;
} }
@@ -1677,21 +1719,25 @@ int sms_out_setup(char *dialing, const char *caller_id, enum number_type caller_
*/ */
void call_out_disconnect(int callref, int cause) void call_out_disconnect(int callref, int cause)
{ {
sender_t *sender; transaction_t *trans;
nmt_t *nmt; nmt_t *nmt;
PDEBUG(DNMT, DEBUG_INFO, "Call has been disconnected by network.\n"); PDEBUG(DNMT, DEBUG_INFO, "Call has been disconnected by network.\n");
for (sender = sender_head; sender; sender = sender->next) { trans = get_transaction_by_callref(callref);
nmt = (nmt_t *) sender; if (!trans) {
if (sender->callref == callref)
break;
}
if (!sender) {
PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing disconnect, but no callref!\n"); PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing disconnect, but no callref!\n");
call_in_release(callref, CAUSE_INVALCALLREF); call_in_release(callref, CAUSE_INVALCALLREF);
return; return;
} }
nmt = trans->nmt;
if (!nmt) {
call_in_release(callref, cause);
trans->callref = 0;
destroy_transaction(trans);
return;
}
/* Release when not active and not waiting for answer */ /* Release when not active and not waiting for answer */
if (nmt->state == STATE_ACTIVE || nmt->state == STATE_MO_COMPLETE) if (nmt->state == STATE_ACTIVE || nmt->state == STATE_MO_COMPLETE)
@@ -1699,39 +1745,41 @@ void call_out_disconnect(int callref, int cause)
switch (nmt->state) { switch (nmt->state) {
case STATE_MT_RINGING: case STATE_MT_RINGING:
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Outgoing disconnect, during ringing, releasing!\n"); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Outgoing disconnect, during ringing, releasing!\n");
trans->callref = 0;
nmt_release(nmt); nmt_release(nmt);
break; break;
default: default:
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Outgoing disconnect, when phone is in call setup, releasing!\n"); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Outgoing disconnect, when phone is in call setup, releasing!\n");
trans->callref = 0;
nmt_release(nmt); nmt_release(nmt);
break; break;
} }
call_in_release(callref, cause); call_in_release(callref, cause);
sender->callref = 0;
} }
/* Call control releases call toward mobile station. */ /* Call control releases call toward mobile station. */
void call_out_release(int callref, int cause) void call_out_release(int callref, int cause)
{ {
sender_t *sender; transaction_t *trans;
nmt_t *nmt; nmt_t *nmt;
PDEBUG(DNMT, DEBUG_INFO, "Call has been released by network, releasing call.\n"); PDEBUG(DNMT, DEBUG_INFO, "Call has been released by network, releasing call.\n");
for (sender = sender_head; sender; sender = sender->next) { trans = get_transaction_by_callref(callref);
nmt = (nmt_t *) sender; if (!trans) {
if (sender->callref == callref)
break;
}
if (!sender) {
PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, but no callref!\n"); PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, but no callref!\n");
/* don't send release, because caller already released */ /* don't send release, because caller already released */
return; return;
} }
nmt = trans->nmt;
sender->callref = 0; trans->callref = 0;
if (!nmt) {
destroy_transaction(trans);
return;
}
switch (nmt->state) { switch (nmt->state) {
case STATE_ACTIVE: case STATE_ACTIVE:
@@ -1752,15 +1800,14 @@ void call_out_release(int callref, int cause)
/* Receive audio from call instance. */ /* Receive audio from call instance. */
void call_rx_audio(int callref, int16_t *samples, int count) void call_rx_audio(int callref, int16_t *samples, int count)
{ {
sender_t *sender; transaction_t *trans;
nmt_t *nmt; nmt_t *nmt;
for (sender = sender_head; sender; sender = sender->next) { trans = get_transaction_by_callref(callref);
nmt = (nmt_t *) sender; if (!trans)
if (sender->callref == callref) return;
break; nmt = trans->nmt;
} if (!nmt)
if (!sender)
return; return;
if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) { if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
@@ -1788,7 +1835,7 @@ int sms_submit(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t orig_t
char sms[512]; char sms[512];
if (!orig_address[0]) if (!orig_address[0])
orig_address = &nmt->subscriber.country; orig_address = &nmt->trans->subscriber.country;
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Received SMS from '%s' to '%s'\n", orig_address, dest_address); PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Received SMS from '%s' to '%s'\n", orig_address, dest_address);
printf("SMS received '%s' -> '%s': %s\n", orig_address, dest_address, message); printf("SMS received '%s' -> '%s': %s\n", orig_address, dest_address, message);

View File

@@ -5,6 +5,7 @@
#include "dms.h" #include "dms.h"
#include "sms.h" #include "sms.h"
enum dsp_mode { enum dsp_mode {
DSP_MODE_DIALTONE, /* stream dial tone to mobile phone */ DSP_MODE_DIALTONE, /* stream dial tone to mobile phone */
DSP_MODE_AUDIO, /* stream audio */ DSP_MODE_AUDIO, /* stream audio */
@@ -63,14 +64,6 @@ typedef struct nmt_sysinfo {
uint8_t area_no; /* Area no. 1..4, 0 = no Area no. */ uint8_t area_no; /* Area no. 1..4, 0 = no Area no. */
} nmt_sysinfo_t; } nmt_sysinfo_t;
typedef struct nmt_subscriber {
/* NOTE: country must be followed by number, so both represent a string */
char country; /* country digit */
char number[7]; /* phone suffix */
char password[4]; /* phone's password + '\0' */
int coinbox; /* phone is a coinbox and accept tariff information */
} nmt_subscriber_t;
const char *nmt_dir_name(enum nmt_direction dir); const char *nmt_dir_name(enum nmt_direction dir);
typedef struct nmt { typedef struct nmt {
@@ -78,26 +71,19 @@ typedef struct nmt {
nmt_sysinfo_t sysinfo; nmt_sysinfo_t sysinfo;
compandor_t cstate; compandor_t cstate;
dtmf_t dtmf; dtmf_t dtmf;
struct transaction *trans; /* pointer to transaction, if bound to channel */
/* sender's states */ /* sender's states */
enum nmt_state state; enum nmt_state state;
int wait_autoanswer; /* wait for frame 15 before we can send autoanswer */ int wait_autoanswer; /* wait for frame 15 before we can send autoanswer */
enum nmt_active_state active_state; enum nmt_active_state active_state;
nmt_subscriber_t subscriber; /* current subscriber */
struct timer timer; struct timer timer;
int rx_frame_count; /* receive frame counter */ int rx_frame_count; /* receive frame counter */
int tx_frame_count; /* transmit frame counter */ int tx_frame_count; /* transmit frame counter */
int tx_callerid_count; /* counter for caller ID repetition */ int tx_callerid_count; /* counter for caller ID repetition */
char dialing[33]; /* dialed digits */ char dialing[33]; /* dialed digits */
char caller_id[33]; /* caller id digits */
enum number_type caller_type; /* caller id type */
int page_try; /* number of paging try */
int mft_num; /* counter for digit for MFT */ int mft_num; /* counter for digit for MFT */
/* special state for paging on different CC */
struct nmt *page_for_nmt; /* only page and assign channel for nmt instance as set */
int mt_channel; /* channel to use */
/* features */ /* features */
int compandor; /* if compandor shall be used */ int compandor; /* if compandor shall be used */
int supervisory; /* if set, use supervisory signal 1..4 */ int supervisory; /* if set, use supervisory signal 1..4 */
@@ -139,15 +125,12 @@ typedef struct nmt {
int super_detected; /* current detection state flag */ int super_detected; /* current detection state flag */
int super_detect_count; /* current number of consecutive detections/losses */ int super_detect_count; /* current number of consecutive detections/losses */
/* DMS states */ /* DMS/SMS states */
int dms_call; /* indicates that this call is a DMS call */
dms_t dms; /* DMS states */ dms_t dms; /* DMS states */
int dms_call; /* indicates that this call is a DMS call */
/* SMS states */
char smsc_number[33]; /* digits to match SMSC */
sms_t sms; /* SMS states */ sms_t sms; /* SMS states */
char smsc_number[33]; /* digits to match SMSC */
struct timer sms_timer; struct timer sms_timer;
char sms_string[256]; /* current string to deliver */
} nmt_t; } nmt_t;
void nmt_channel_list(void); void nmt_channel_list(void);
@@ -160,9 +143,11 @@ uint8_t nmt_country_by_short_name(const char *short_name);
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 compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback); 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 compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback);
void nmt_check_channels(void); void nmt_check_channels(void);
void nmt_destroy(sender_t *sender); void nmt_destroy(sender_t *sender);
void nmt_go_idle(nmt_t *nmt);
void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed); void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed);
const char *nmt_get_frame(nmt_t *nmt); const char *nmt_get_frame(nmt_t *nmt);
void nmt_rx_super(nmt_t *nmt, int tone, double quality); void nmt_rx_super(nmt_t *nmt, int tone, double quality);
void timeout_mt_paging(struct transaction *trans);
void deliver_sms(const char *sms); void deliver_sms(const char *sms);
int submit_sms(const char *sms); int submit_sms(const char *sms);

144
src/nmt/transaction.c Normal file
View File

@@ -0,0 +1,144 @@
/* NMT transaction 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 "../common/debug.h"
#include "../common/timer.h"
#include "nmt.h"
#include "transaction.h"
static transaction_t *trans_list = NULL;
static void transaction_timeout(struct timer *timer);
/* link transaction to list */
static void link_transaction(transaction_t *trans)
{
transaction_t **transp;
/* attach to end of list, so first transaction is served first */
PDEBUG(DTRANS, DEBUG_DEBUG, "Linking transaction %p to list\n", trans);
trans->next = NULL;
transp = &trans_list;
while (*transp)
transp = &((*transp)->next);
*transp = trans;
}
/* unlink transaction from list */
static void unlink_transaction(transaction_t *trans)
{
transaction_t **transp;
sender_t *sender;
nmt_t *nmt;
/* unlink */
PDEBUG(DTRANS, DEBUG_DEBUG, "Unlinking transaction %p from list\n", trans);
transp = &trans_list;
while (*transp && *transp != trans)
transp = &((*transp)->next);
if (!(*transp)) {
PDEBUG(DTRANS, DEBUG_ERROR, "Transaction not in list, please fix!!\n");
abort();
}
*transp = trans->next;
trans->next = NULL;
/* unbind from channel */
trans->nmt = NULL;
for (sender = sender_head; sender; sender = sender->next) {
nmt = (nmt_t *)sender;
if (nmt->trans == trans)
nmt_go_idle(nmt);
}
}
/* create transaction */
transaction_t *create_transaction(struct nmt_subscriber *subscr)
{
transaction_t *trans;
trans = calloc(1, sizeof(*trans));
if (!trans) {
PDEBUG(DTRANS, DEBUG_ERROR, "No memory!\n");
return NULL;
}
timer_init(&trans->timer, transaction_timeout, trans);
memcpy(&trans->subscriber, subscr, sizeof(struct nmt_subscriber));
PDEBUG(DTRANS, DEBUG_INFO, "Created transaction for subscriber '%c,%s'\n", subscr->country, subscr->number);
link_transaction(trans);
return trans;
}
/* destroy transaction */
void destroy_transaction(transaction_t *trans)
{
unlink_transaction(trans);
PDEBUG(DTRANS, DEBUG_INFO, "Destroying transaction for subscriber '%c,%s'\n", trans->subscriber.country, trans->subscriber.number);
timer_exit(&trans->timer);
free(trans);
}
/* Timeout handling */
static void transaction_timeout(struct timer *timer)
{
transaction_t *trans = (transaction_t *)timer->priv;
timeout_mt_paging(trans);
}
transaction_t *get_transaction_by_callref(int callref)
{
transaction_t *trans;
trans = trans_list;
while (trans) {
if (trans->callref == callref)
break;
trans = trans->next;
}
return trans;
}
transaction_t *get_transaction_by_number(struct nmt_subscriber *subscr)
{
transaction_t *trans;
trans = trans_list;
while (trans) {
if (trans->subscriber.country == subscr->country
&& !strcmp(trans->subscriber.number, subscr->number))
break;
trans = trans->next;
}
return trans;
}

32
src/nmt/transaction.h Normal file
View File

@@ -0,0 +1,32 @@
/* info about subscriber */
typedef struct nmt_subscriber {
/* NOTE: country must be followed by number, so both represent a string */
char country; /* country digit */
char number[7]; /* phone suffix */
char password[4]; /* phone's password + '\0' */
int coinbox; /* phone is a coinbox and accept tariff information */
} nmt_subscriber_t;
/* transaction node */
typedef struct transaction {
struct transaction *next; /* pointer to next node in list */
nmt_t *nmt; /* pointer to nmt instance, if bound to a channel */
int callref; /* callref for transaction */
struct nmt_subscriber subscriber;
struct timer timer;
int page_try; /* number of paging try */
/* caller ID */
char caller_id[33]; /* caller id digits */
enum number_type caller_type; /* caller id type */
/* SMS */
char sms_string[256]; /* current string to deliver */
} transaction_t;
transaction_t *create_transaction(struct nmt_subscriber *subscriber);
void destroy_transaction(transaction_t *trans);
transaction_t *get_transaction_by_callref(int callref);
transaction_t *get_transaction_by_number(struct nmt_subscriber *subscr);