osmocom-analog/src/mpt1327/mpt1327.c

1669 lines
56 KiB
C
Executable File

/* protocol handling
*
* (C) 2021 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/>.
*/
/*
* How code-word scheduling works on downlink (tx_sched):
*
* The DSP mode can be set to CONTROL or TRAFFIC, depending on the mode the
* channel is working. Depending on that, a function is called for every
* code-word to be transmitted, one to schedule code-words on control channel,
* one to schedule code-words on traffic channel.
*
* The scheduler uses a state that indicates what was last scheduled, i.e. what
* is currently transmitted. If nothing is scheduled yet, an IDLE state is set.
* When switching between CONTROL and TRAFFIC mode, the different states (for
* each mode) are handled as they would be IDLE state, so that no reset to IDLE
* state is required when changing DSP mode.
*
* An IDLE state results in a startup sequence on control channel (SYNC) or on
* traffic channel (SYNT), whenever a message must be scheduled. The message to
* be scheduled depends on the unit states. All units are queried for any
* message to be scheduled. If no message on control channel need to be
* scheduled, an ALH message is scheduled, so that random access is possible.
* On control channel the address conde-words alternate with CCSC code-word.
*
* To prevent random access when a unit is requested to transmit more than two
* code-word, a dummy frame counter is set. Then a dummy AHY message is
* scheduled, to prevent random access by other units in that slot.
*/
/*
* How code-word scheduling works on uplink (rx_sched):
*
* The DSP mode can be set to CONTROL or TRAFFIC, depending on the mode the
* channel is working. Depending on that, a function is called for every
* code-word received, one for code-words on control channel, one for
* code-words on traffic channel.
*
* Most messages have an address code-word only, so the message type is defined
* by the elements in the code-word. Additional data code-words (that may
* follow an address code-word) do not have a message type, because they are
* defined by the previous address code-word. If a message has additional data
* code-words, a data word counter is set, so that subsequent data code-words
* are parsed as defined by the address code-word. In case of a CRC error, the
* message resets into un-synced state, i.e. waiting for next sync + address
* code-word.
*/
#define CHAN mpt1327->sender.kanal
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <inttypes.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libmobile/call.h"
#include "../libmobile/cause.h"
#include "../libosmocc/message.h"
#include "mpt1327.h"
#include "dsp.h"
#include "message.h"
/* Timers and counters */
#define RESPONSE_TIMEOUT 1.0
#define REPEAT_GTC 1
#define REPEAT_AHY 1
#define REPEAT_AHYC 1
#define REPEAT_AHYX 3
#define REPEAT_CLEAR 3
/* check if number is a valid station ID */
const char *mpt1327_number_valid(const char *number)
{
int value;
static char error[256];
/* assume that the number has valid length(s) and digits */
value = (number[0] - '0') * 100 + (number[1] - '0') * 10 + (number[2] - '0');
if (value > 127) {
sprintf(error, "Prefix '%03d' is not in range 000..127.", value);
return error;
}
value = atoi(number + 3);
if (value > 8100 || value < 1) {
sprintf(error, "Ident '%04d' is not in range 0001..8100.", value);
return error;
}
return NULL;
}
/*
* Sysdef
*/
static mpt1327_sysdef_t sysdef;
void init_sysdef (uint16_t sys, int wt, int per, int pon, int timeout)
{
memset(&sysdef, 0, sizeof(sysdef));
sysdef.sys = sys;
sysdef.wt = wt;
sysdef.per = per;
sysdef.pon = pon;
sysdef.timeout = timeout;
sysdef.framelength = 3;
sysdef.bcast_slots = 10; /* every seconds is good */
}
/*
* Units handling
*/
static mpt1327_unit_t *unit_list = NULL;
#define UNIT_IDLE 0
#define UNIT_REGISTER_ACK (1 << 0) /* need to ack registration */
#define UNIT_DIVERSION_REJ (1 << 1) /* need to nack diversion */
#define UNIT_CALLING_REJ (1 << 2) /* need to reject call */
#define UNIT_CALLING_AHYC (1 << 3) /* need to request SAMIS */
#define UNIT_CALLING_SAMIS (1 << 4) /* wait for SAMIS response */
#define UNIT_CALLED_AHY (1 << 5) /* need to request ACK */
#define UNIT_CALLED_AHYX (1 << 6) /* cancel call */
#define UNIT_CALLED_ACK (1 << 7) /* wait for AHY response */
#define UNIT_GTC_P (1 << 8) /* need to assign channel (same prefix) */
#define UNIT_GTC_A (1 << 9) /* need to assign channel (calling unit) */
#define UNIT_GTC_B (1 << 10) /* need to assign channel (called unit) */
#define UNIT_CALL (1 << 11) /* established call */
#define UNIT_CALL_CLEAR (1 << 12) /* established call */
#define UNIT_CANCEL_ACK (1 << 13) /* need to ack cancelation */
const char *unit_state_name(uint64_t state)
{
static char invalid[32];
switch (state) {
case UNIT_IDLE:
return "IDLE";
case UNIT_REGISTER_ACK:
return "REGISTER-ACK";
case UNIT_DIVERSION_REJ:
return "DIVERSION-REJ";
case UNIT_CALLING_REJ:
return "CALLING-REJ";
case UNIT_CALLING_AHYC:
return "CALLING-AHYC";
case UNIT_CALLING_SAMIS:
return "CALLING-SAMIS";
case UNIT_CALLED_AHY:
return "CALLED-AHY";
case UNIT_CALLED_AHYX:
return "CALLED-AHYX";
case UNIT_CALLED_ACK:
return "CALLED-ACK";
case UNIT_GTC_P:
return "GTC-BOTH";
case UNIT_GTC_A:
return "GTC-OTHER-UNIT";
case UNIT_GTC_B:
return "GTC-UNIT";
case UNIT_CALL:
return "CALL";
case UNIT_CALL_CLEAR:
return "CALL-CLEAR";
case UNIT_CANCEL_ACK:
return "CANCEL-ACK";
}
sprintf(invalid, "invalid(0x%" PRIx64 ")", state);
return invalid;
}
void unit_new_state(mpt1327_unit_t *unit, uint64_t new_state)
{
PDEBUG(DMPT1327, DEBUG_DEBUG, "Radio Unit (Prefix:%d Ident:%d) state: %s -> %s\n", unit->prefix, unit->ident, unit_state_name(unit->state), unit_state_name(new_state));
unit->state = new_state;
}
static void unit_timeout(struct timer *timer);
mpt1327_unit_t *get_unit(uint8_t prefix, uint16_t ident)
{
mpt1327_unit_t **unitp;
for (unitp = &unit_list; *unitp; unitp = &((*unitp)->next)) {
if ((*unitp)->prefix == prefix
&& (*unitp)->ident == ident)
break;
}
if (!(*unitp)) {
PDEBUG(DDB, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) added to database\n", prefix, ident);
*unitp = calloc(1, sizeof(mpt1327_unit_t));
timer_init(&(*unitp)->timer, unit_timeout, (*unitp));
(*unitp)->state = UNIT_IDLE;
(*unitp)->prefix = prefix;
(*unitp)->ident = ident;
}
return *unitp;
}
mpt1327_unit_t *find_unit_state(uint32_t state, mpt1327_t *tc)
{
mpt1327_unit_t *unit;
for (unit = unit_list; unit; unit = unit->next) {
if (tc && unit->tc != tc)
continue;
if ((unit->state & state))
break;
}
return unit;
}
mpt1327_unit_t *find_unit_callref(uint32_t callref)
{
mpt1327_unit_t *unit;
for (unit = unit_list; unit; unit = unit->next) {
if ((unit->callref & callref))
break;
}
return unit;
}
static void mpt1327_go_idle(mpt1327_t *mpt1327);
static void mpt1327_release(mpt1327_unit_t *unit);
/* Timeout handling */
static void unit_timeout(struct timer *timer)
{
mpt1327_unit_t *unit = (mpt1327_unit_t *)timer->priv;
// FIXME: do some retry
switch (unit->state) {
case UNIT_CALLING_SAMIS:
if (unit->repeat) {
--unit->repeat;
PDEBUG(DMPT1327, DEBUG_INFO, "Resend AHYC, because unit timed out.\n");
unit_new_state(unit, UNIT_CALLING_AHYC);
break;
}
PDEBUG(DMPT1327, DEBUG_INFO, "Unit failed to respond to AHYC, releasing...\n");
mpt1327_release(unit);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
break;
case UNIT_CALLED_ACK:
if (unit->repeat) {
--unit->repeat;
PDEBUG(DMPT1327, DEBUG_INFO, "Resend AHY, because unit timed out.\n");
unit_new_state(unit, UNIT_CALLED_AHY);
break;
}
PDEBUG(DMPT1327, DEBUG_INFO, "Unit failed to respond to AHY, releasing...\n");
mpt1327_release(unit);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
break;
case UNIT_CALL:
PDEBUG(DMPT1327, DEBUG_NOTICE, "Release call, because unit timed out.\n");
mpt1327_release(unit);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
break;
default:
PDEBUG(DMPT1327, DEBUG_ERROR, "Unknown timeout at state 0x%" PRIx64 ", please fix!\n", unit->state);
break;
}
}
void flush_units(void)
{
mpt1327_unit_t *next;
while (unit_list) {
next = unit_list->next;
timer_exit(&unit_list->timer);
free(unit_list);
unit_list = next;
}
}
void dump_units(void)
{
mpt1327_unit_t *unit = unit_list;
PDEBUG(DDB, DEBUG_NOTICE, "Dump of Radio Unit list:\n");
if (!unit) {
PDEBUG(DDB, DEBUG_NOTICE, " - No Radio Unit seen yet!\n");
return;
}
while (unit) {
PDEBUG(DDB, DEBUG_NOTICE, " - Radio Unit (Prefix:%d Ident:%d) seen on this TSC.\n", unit->prefix, unit->ident);
unit = unit->next;
}
}
/*
* bands and channels
*/
static struct mpt1327_band_def {
const char *name;
const char *description;
} mpt1327_band_def[] = {
{ "MPT1343/1", "MPT1343 Sub Band 1"},
{ "MPT1343/2", "MPT1343 Sub Band 2"},
{ "Regionet43/1", "Regionet43 410-430 MHz (German band)" },
{ "Regionet43/2", "Regionet43 445-448 MHz (rarely used in Germany)" },
{ NULL, NULL }
};
const char *mpt1327_band_name(enum mpt1327_band band)
{
return mpt1327_band_def[band].name;
}
int mpt1327_band_by_short_name(const char *name)
{
int i;
for (i = 0; mpt1327_band_def[i].name; i++) {
if (!strcasecmp(mpt1327_band_def[i].name, name))
return i;
}
return -1;
}
void mpt1327_band_list(void)
{
int i;
printf("Name\t\tDescription\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; mpt1327_band_def[i].name; i++)
printf("%s\t%s\n", mpt1327_band_def[i].name, mpt1327_band_def[i].description);
}
/* convert channel to frequency */
double mpt1327_channel2freq(enum mpt1327_band band, int channel, int uplink)
{
double freq = 0, offset = 0; // make GCC happy
int channels = 0;
switch(band) {
case BAND_MPT1343_SUB1:
freq = 177.2125;
offset = 8.0; /* that's right! */
channel -= 58;
channels = 503;
break;
case BAND_MPT1343_SUB2:
freq = 201.2125;
offset = -8.0;
channel -= 58;
channels = 503;
break;
case BAND_REGIONET43_SUB1:
freq = 420.0125;
offset = -10.0;
channel -= 1;
channels = 799;
break;
case BAND_REGIONET43_SUB2:
freq = 445.0125;
offset = -5.0;
channel -= 1;
channels = 239;
break;
}
/* channel out of range */
if (channel < 0 || channel > channels)
return 0.0;
if (uplink == 2)
return offset * 1e6;
freq += channel * 0.0125;
if (uplink)
freq += offset;
return freq * 1e6;
}
/* convert channel to chan field */
uint16_t mpt1327_channel2chan(enum mpt1327_band band, int channel)
{
uint16_t chan = 0;
switch(band) {
case BAND_MPT1343_SUB1:
chan = channel - 58 + 513;
break;
case BAND_MPT1343_SUB2:
chan = channel - 58 + 1;
break;
case BAND_REGIONET43_SUB1:
chan = channel - 1 + 1;
break;
case BAND_REGIONET43_SUB2:
chan = channel - 1 + 1; // works with DETEWE
break;
}
return chan;
}
static struct mpt1327_channels {
enum mpt1327_chan_type chan_type;
const char *short_name;
const char *long_name;
} mpt1327_channels[] = {
{ CHAN_TYPE_CC, "CC", "control channel" },
{ CHAN_TYPE_TC, "TC", "traffic channel" },
{ CHAN_TYPE_CC_TC, "CC/TC","combined control & traffic channel" },
{ 0, NULL, NULL }
};
void mpt1327_channel_list(void)
{
int i;
printf("Type\t\tDescription\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; mpt1327_channels[i].long_name; i++)
printf("%s%s\t%s\n", mpt1327_channels[i].short_name, (strlen(mpt1327_channels[i].short_name) >= 8) ? "" : "\t", mpt1327_channels[i].long_name);
}
int mpt1327_channel_by_short_name(const char *short_name)
{
int i;
for (i = 0; mpt1327_channels[i].short_name; i++) {
if (!strcasecmp(mpt1327_channels[i].short_name, short_name)) {
PDEBUG(DMPT1327, DEBUG_INFO, "Selecting channel '%s' = %s\n", mpt1327_channels[i].short_name, mpt1327_channels[i].long_name);
return mpt1327_channels[i].chan_type;
}
}
return -1;
}
const char *chan_type_short_name(enum mpt1327_chan_type chan_type)
{
int i;
for (i = 0; mpt1327_channels[i].short_name; i++) {
if (mpt1327_channels[i].chan_type == chan_type)
return mpt1327_channels[i].short_name;
}
return "invalid";
}
const char *chan_type_long_name(enum mpt1327_chan_type chan_type)
{
int i;
for (i = 0; mpt1327_channels[i].long_name; i++) {
if (mpt1327_channels[i].chan_type == chan_type)
return mpt1327_channels[i].long_name;
}
return "invalid";
}
/*
* MPT processing
*/
static mpt1327_t *search_free_tc(void)
{
sender_t *sender;
mpt1327_t *tc, *cc_tc = NULL;
for (sender = sender_head; sender; sender = sender->next) {
tc = (mpt1327_t *) sender;
if (tc->state != STATE_IDLE)
continue;
/* remember combined voice/control/paging channel as second alternative */
if (tc->chan_type == CHAN_TYPE_CC_TC)
cc_tc = tc;
if (tc->chan_type == CHAN_TYPE_TC)
return tc;
}
return cc_tc;
}
static mpt1327_t *search_cc(void)
{
sender_t *sender;
mpt1327_t *cc = NULL;
for (sender = sender_head; sender; sender = sender->next) {
cc = (mpt1327_t *) sender;
/* remember combined voice/control/paging channel as second alternative */
if (cc->chan_type == CHAN_TYPE_CC_TC)
return cc;
if (cc->chan_type == CHAN_TYPE_CC)
return cc;
}
return NULL;
}
const char *mpt1327_state_name(enum mpt1327_state state)
{
static char invalid[16];
switch (state) {
case STATE_NULL:
return "(NULL)";
case STATE_IDLE:
return "IDLE";
case STATE_BUSY:
return "BUSY";
}
sprintf(invalid, "invalid(%d)", state);
return invalid;
}
void mpt1327_display_status(void)
{
sender_t *sender;
mpt1327_t *mpt1327;
display_status_start();
for (sender = sender_head; sender; sender = sender->next) {
mpt1327 = (mpt1327_t *) sender;
display_status_channel(mpt1327->sender.kanal, chan_type_short_name(mpt1327->chan_type), mpt1327_state_name(mpt1327->state));
if (mpt1327->unit) {
char unit_id[32];
sprintf(unit_id, "%d/%d", mpt1327->unit->prefix, mpt1327->unit->ident);
display_status_subscriber(unit_id, NULL);
}
}
display_status_end();
}
static void mpt1327_new_state(mpt1327_t *mpt1327, enum mpt1327_state new_state, mpt1327_unit_t *unit)
{
if (mpt1327->state == new_state)
return;
PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "State change: %s -> %s\n", mpt1327_state_name(mpt1327->state), mpt1327_state_name(new_state));
/* unlink unit, if linked */
if (mpt1327->unit) {
mpt1327->unit->tc = NULL;
mpt1327->unit = NULL;
}
/* link unit, if given */
if (unit) {
unit->tc = mpt1327;
mpt1327->unit = unit;
}
mpt1327->state = new_state;
mpt1327_display_status();
}
static void mpt1327_timeout(struct timer *timer);
/* Create transceiver instance and link to a list. */
int mpt1327_create(enum mpt1327_band band, const char *kanal, enum mpt1327_chan_type chan_type, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double squelch_db)
{
sender_t *sender;
mpt1327_t *mpt1327;
int rc;
/* check channel matching and set deviation factor */
if (mpt1327_channel2freq(band, atoi(kanal), 0) == 0.0)
return -EINVAL;
for (sender = sender_head; sender; sender = sender->next) {
mpt1327 = (mpt1327_t *)sender;
if ((mpt1327->chan_type == CHAN_TYPE_CC || mpt1327->chan_type == CHAN_TYPE_CC_TC)
&& (chan_type == CHAN_TYPE_CC || chan_type == CHAN_TYPE_CC_TC)) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "More than one control channel is not supported, please define other channels as traffic channels!\n");
return -EINVAL;
}
}
mpt1327 = calloc(1, sizeof(mpt1327_t));
if (!mpt1327) {
PDEBUG(DMPT1327, DEBUG_ERROR, "No memory!\n");
return -EIO;
}
PDEBUG(DMPT1327, DEBUG_DEBUG, "Creating 'MPT1327' instance for Channel %s on Band %s (sample rate %d).\n", kanal, mpt1327_band_def[band].name, samplerate);
/* init general part of transceiver */
rc = sender_create(&mpt1327->sender, kanal, mpt1327_channel2freq(band, atoi(kanal), 0), mpt1327_channel2freq(band, atoi(kanal), 1), device, use_sdr, samplerate, rx_gain, tx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DMPT1327, DEBUG_ERROR, "Failed to init 'Sender' processing!\n");
goto error;
}
/* init audio processing */
rc = dsp_init_sender(mpt1327, squelch_db);
if (rc < 0) {
PDEBUG(DANETZ, DEBUG_ERROR, "Failed to init signal processing!\n");
goto error;
}
/* timers */
timer_init(&mpt1327->timer, mpt1327_timeout, mpt1327);
mpt1327->band = band;
mpt1327->chan_type = chan_type;
/* only accept these valued */
if (sysdef.framelength != 1 && sysdef.framelength != 3 && sysdef.framelength != 6) {
PDEBUG(DMPT1327, DEBUG_ERROR, "Invalid frame length %d, please fix!\n", sysdef.framelength);
abort();
}
if (sysdef.wt != 5 && sysdef.wt != 10 && sysdef.wt != 15) {
PDEBUG(DMPT1327, DEBUG_ERROR, "Invalid WT value %d, please fix!\n", sysdef.wt);
abort();
}
/* go into idle state */
mpt1327_go_idle(mpt1327);
PDEBUG(DMPT1327, DEBUG_NOTICE, "Created channel #%s of type '%s' = %s\n", kanal, chan_type_short_name(chan_type), chan_type_long_name(chan_type));
return 0;
error:
mpt1327_destroy(&mpt1327->sender);
return rc;
}
void mpt1327_check_channels(void)
{
sender_t *sender;
mpt1327_t *mpt1327;
int cc = 0, tc = 0;
int note = 0;
for (sender = sender_head; sender; sender = sender->next) {
mpt1327 = (mpt1327_t *) sender;
if (mpt1327->chan_type == CHAN_TYPE_CC)
cc = 1;
if (mpt1327->chan_type == CHAN_TYPE_TC)
tc = 1;
if (mpt1327->chan_type == CHAN_TYPE_CC_TC) {
cc = 1;
tc = 1;
}
}
if (cc && !tc) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Selected channel(s) can be used for control only.\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** No call is possible.\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Use at least one 'TC'!\n");
note = 1;
}
if (tc && !cc) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Selected channel(s) can be used for traffic only.\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** No call to the mobile phone is possible.\n");
PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Use one 'CC'!\n");
note = 1;
}
if (note)
PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
}
/* Destroy transceiver instance and unlink from list. */
void mpt1327_destroy(sender_t *sender)
{
mpt1327_t *mpt1327 = (mpt1327_t *) sender;
PDEBUG(DMPT1327, DEBUG_DEBUG, "Destroying 'MPT1327' instance for channel = %s.\n", sender->kanal);
dsp_cleanup_sender(mpt1327);
timer_exit(&mpt1327->timer);
sender_destroy(&mpt1327->sender);
free(sender);
}
/* Abort connection towards mobile station changing to IDLE state */
static void mpt1327_go_idle(mpt1327_t *mpt1327)
{
timer_stop(&mpt1327->timer);
mpt1327->pressel_on = 0;
PDEBUG(DMPT1327, DEBUG_INFO, "Entering IDLE state on channel %s.\n", mpt1327->sender.kanal);
mpt1327_new_state(mpt1327, STATE_IDLE, NULL);
memset(&mpt1327->tx_sched, 0, sizeof(mpt1327->tx_sched));
switch (mpt1327->chan_type) {
case CHAN_TYPE_CC:
case CHAN_TYPE_CC_TC:
mpt1327->tx_sched.state = SCHED_STATE_CC_IDLE;
mpt1327_set_dsp_mode(mpt1327, DSP_MODE_CONTROL, 0);
break;
case CHAN_TYPE_TC:
mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
mpt1327_set_dsp_mode(mpt1327, DSP_MODE_OFF, 0);
break;
}
}
static void mpt1327_release(mpt1327_unit_t *unit)
{
timer_stop(&unit->timer);
if (unit->state == UNIT_CALL && unit->tc) {
/* release all units on traffic channel */
unit_new_state(unit, UNIT_CALL_CLEAR);
unit->repeat = REPEAT_CLEAR;
} else {
/* release unit on control channel */
unit_new_state(unit, UNIT_CALLED_AHYX);
unit->repeat = REPEAT_AHYX;
}
}
static int gtc_aloha_number(int length)
{
switch (length) {
case 1:
return 1;
case 3:
return 2;
case 6:
return 3;
default:
return 0;
}
}
/* schedule message on control channel
*
* on IDLE state a STARTUP sequence is sent, followed by alternating CCSC and
* ADDR codewords. if a unit has nothing to send, ALH is transmitted. if a
* unit wants to send a message, the message is scheduled. this message can
* be repeated. afterwards, if no unit has something to send, the ALH is
* scheduled.
*
* a dummy slot is used to allow radio unit to allow multi slot response to a
* request from TSC.
*/
int mpt1327_send_codeword_control(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
{
mpt1327_unit_t *unit;
/* CC scheduler, the sched_state is what we have sent */
switch (mpt1327->tx_sched.state) {
case SCHED_STATE_CC_ADDR:
codeword->type = MPT_CCSC;
codeword->params[MPT_SYS] = sysdef.sys;
mpt1327->tx_sched.state = SCHED_STATE_CC_CCSC;
break;
case SCHED_STATE_CC_STARTUP:
case SCHED_STATE_CC_CCSC:
/* count slots for each broadcast */
if (mpt1327->tx_sched.bcast_count < sysdef.bcast_slots) {
mpt1327->tx_sched.bcast_count++;
}
/* count slots in frame */
if (!mpt1327->tx_sched.frame_length || mpt1327->tx_sched.frame_count == mpt1327->tx_sched.frame_length) {
mpt1327->tx_sched.frame_length = sysdef.framelength;
mpt1327->tx_sched.frame_count = 0;
}
/* send out a dummy slot to prevent random access from other units */
if (mpt1327->tx_sched.dummy_slot) {
mpt1327->tx_sched.dummy_slot--;
/* if this is a new frame, make it 1 slot long */
if (mpt1327->tx_sched.frame_count == 0)
mpt1327->tx_sched.frame_length = 1;
codeword->type = MPT_AHY;
codeword->params[MPT_PFIX] = 0x2a; /* just some alternating pattern (ignored) */
codeword->params[MPT_IDENT1] = IDENT_DUMMYI;
codeword->params[MPT_IDENT2] = IDENT_DUMMYI;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending dummy AHY, to prevent random access while receiving SAMIS\n");
} else {
unit = find_unit_state(UNIT_REGISTER_ACK | UNIT_DIVERSION_REJ | UNIT_CALLING_REJ | UNIT_CALLING_AHYC | UNIT_CALLED_AHY | UNIT_GTC_P | UNIT_GTC_A | UNIT_GTC_B | UNIT_CANCEL_ACK | UNIT_CALLED_AHYX, NULL);
if (!unit) {
/* if we reached the slot count for broadcast message
* AND the frame is not frame 0 (with given length, as long es frame length > 1)
* if the frame length is 1 (implies count == 0), then make this message single slot frame
*/
if (mpt1327->tx_sched.bcast_count == sysdef.bcast_slots
&& (mpt1327->tx_sched.frame_count > 0 || mpt1327->tx_sched.frame_length == 1)) {
mpt1327->tx_sched.bcast_count = 0;
/* if this is a new frame, make it 1 slot long */
if (mpt1327->tx_sched.frame_count == 0)
mpt1327->tx_sched.frame_length = 1;
codeword->type = MPT_BCAST2;
codeword->params[MPT_SYS] = sysdef.sys;
codeword->params[MPT_IVAL] = sysdef.per;
if (!sysdef.per)
codeword->params[MPT_PER] = 1;
if (!sysdef.pon)
codeword->params[MPT_PON] = 1;
} else {
codeword->type = MPT_ALH;
codeword->params[MPT_PFIX] = 0x2a; /* just some alternating pattern (ignored) */
codeword->params[MPT_IDENT1] = 0x1555; /* dito */
codeword->params[MPT_CHAN4] = mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal)) & 0xf;
codeword->params[MPT_WT] = (sysdef.wt < 5) ? sysdef.wt : sysdef.wt / 5 + 4;
}
} else switch (unit->state) {
case UNIT_REGISTER_ACK: /* ack to register */
codeword->type = MPT_ACK;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = IDENT_REGI;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_QUAL] = 0;
unit_new_state(unit, UNIT_IDLE);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
break;
case UNIT_DIVERSION_REJ: /* reject diversion */
codeword->type = MPT_ACKX;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_QUAL] = 0;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending negative acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_IDLE);
if (unit->tc)
mpt1327_go_idle(unit->tc);
break;
case UNIT_CALLING_REJ: /* outgoing call rejected, no channel available */
codeword->type = MPT_ACKX;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_QUAL] = 1;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending negative acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_IDLE);
if (unit->tc)
mpt1327_go_idle(unit->tc);
break;
case UNIT_CALLING_AHYC: /* request SAMIS for dialing data */
codeword->type = MPT_AHYC;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = unit->ident; /* implies Mode 1 */
switch (unit->called_type) {
case CALLED_TYPE_INTERPFX:
codeword->params[MPT_SLOTS] = 0x1;
codeword->params[MPT_DESC] = 0x0;
break;
case CALLED_TYPE_PSTN_LONG1:
codeword->params[MPT_SLOTS] = 0x1;
codeword->params[MPT_DESC] = 0x1;
break;
case CALLED_TYPE_PSTN_LONG2:
codeword->params[MPT_SLOTS] = 0x2;
codeword->params[MPT_DESC] = 0x1;
mpt1327->tx_sched.dummy_slot = 1;
break;
case CALLED_TYPE_PBX_LONG:
codeword->params[MPT_SLOTS] = 0x1;
codeword->params[MPT_DESC] = 0x2;
break;
default:
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Want to send AHYC, but called_type not set correctly, please fix!\n");
abort();
}
unit_new_state(unit, UNIT_CALLING_SAMIS);
mpt1327->rx_sched.data_prefix = unit->prefix;
mpt1327->rx_sched.data_ident = unit->ident;
PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "Starting timer, waiting for response\n");
timer_start(&unit->timer, RESPONSE_TIMEOUT);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHYC, to request SAMIS from Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
break;
case UNIT_CALLED_AHY: /* call to unit and request ACK from unit */
codeword->type = MPT_AHY;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->ident;
codeword->params[MPT_IDENT2] = IDENT_PABXI;
codeword->params[MPT_D] = 0; /* speech call */
codeword->params[MPT_POINT] = 0; /* demand ACK from ident1 */
codeword->params[MPT_CHECK] = 1; /* unit is in contact and accepts calls */
codeword->params[MPT_E] = 0; /* no emergency call */
codeword->params[MPT_AD] = 0; /* no appended data */
unit_new_state(unit, UNIT_CALLED_ACK);
PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "Starting timer, waiting for response\n");
timer_start(&unit->timer, RESPONSE_TIMEOUT);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHY, to request ACK from Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
break;
case UNIT_GTC_P: /* channel assignment to unit itself and called unit */
codeword->type = MPT_GTC;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to calling and called Radio Units (Prefix:%d Ident:%d and Ident:%d)\n", unit->prefix, unit->ident, unit->called_ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_CALL);
mpt1327_set_dsp_mode(unit->tc, DSP_MODE_TRAFFIC, 1);
if (sysdef.timeout)
timer_start(&unit->timer, sysdef.timeout);
break;
case UNIT_GTC_B: /* channel assignment to called unit */
/* NOTE GTC to called unit must be sent before GTC to calling unit (1.3.5.3) */
codeword->type = MPT_GTC;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = IDENT_DUMMYI;
codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to called Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
break;
case UNIT_GTC_A: /* channel assignment unit itself */
codeword->type = MPT_GTC;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = IDENT_DUMMYI;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to calling Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_CALL);
mpt1327_set_dsp_mode(unit->tc, DSP_MODE_TRAFFIC, 1);
if (sysdef.timeout)
timer_start(&unit->timer, sysdef.timeout);
break;
case UNIT_CANCEL_ACK:
codeword->type = MPT_ACK;
codeword->params[MPT_PFIX] = unit->called_prefix;
codeword->params[MPT_IDENT1] = unit->called_ident;
codeword->params[MPT_IDENT2] = unit->ident;
codeword->params[MPT_QUAL] = 1;
unit_new_state(unit, UNIT_IDLE);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
break;
case UNIT_CALLED_AHYX: /* cancel call towards unit */
codeword->type = MPT_AHYX;
codeword->params[MPT_PFIX] = unit->prefix;
codeword->params[MPT_IDENT1] = unit->ident;
codeword->params[MPT_IDENT2] = IDENT_PABXI;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHYX, to cancel call to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
if (unit->repeat) {
--unit->repeat;
break;
}
unit_new_state(unit, UNIT_IDLE);
if (unit->tc)
mpt1327_go_idle(unit->tc);
break;
}
}
if (codeword->type == MPT_GTC)
codeword->params[MPT_N] = (mpt1327->tx_sched.frame_count == 0) ? gtc_aloha_number(mpt1327->tx_sched.frame_length) : 0;
else
codeword->params[MPT_N] = (mpt1327->tx_sched.frame_count == 0) ? mpt1327->tx_sched.frame_length : 0;
mpt1327->tx_sched.frame_count++;
mpt1327->tx_sched.state = SCHED_STATE_CC_ADDR;
break;
default:
/* on dirty state (e.g. changing from TC to CC), we start control channel framing */
codeword->type = MPT_START_SYNC;
mpt1327->tx_sched.frame_length = 0;
mpt1327->tx_sched.state = SCHED_STATE_CC_STARTUP;
break;
}
return 0;
}
/* schedule messages on traffic channel
*
* when the unit has a message to send, it, the repeat counter is decreased and
* a SYNC is sent the next request will send the an ADDR codeword. this will
* repeat until the repeat counter reaches 0.
*/
int mpt1327_send_codeword_traffic(mpt1327_t *mpt1327, mpt1327_codeword_t __attribute__((unused)) *codeword)
{
mpt1327_unit_t *unit;
mpt1327_t *cc;
/* TC scheduler */
switch (mpt1327->tx_sched.state) {
case SCHED_STATE_TC_IDLE:
case SCHED_STATE_TC_ADDR:
/* on idle state or after sending address, we search for a unit that wants to send a message */
unit = find_unit_state(UNIT_CALL_CLEAR, mpt1327);
if (!unit) {
/* no message, so we have nothing to send */
mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
return -1;
}
switch (unit->state) {
case UNIT_CALL_CLEAR: /* release channel */
if (!unit->repeat) {
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Done sending clear down on traffic channel, releasing\n");
unit_new_state(unit, UNIT_IDLE);
mpt1327_go_idle(mpt1327);
return -1;
}
--unit->repeat;
break;
}
codeword->type = MPT_START_SYNT;
mpt1327->tx_sched.state = SCHED_STATE_TC_SYNT;
break;
case SCHED_STATE_TC_SYNT:
/* after sending SYNT, we process message that unit wants to send */
unit = find_unit_state(UNIT_CALL_CLEAR, mpt1327);
if (!unit) {
mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
return -1;
}
switch (unit->state) {
case UNIT_CALL_CLEAR: /* release channel */
codeword->type = MPT_CLEAR;
codeword->params[MPT_CHAN] = mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal));
cc = search_cc();
if (cc)
codeword->params[MPT_CONT] = mpt1327_channel2chan(cc->band, atoi(cc->sender.kanal));
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending clear down on traffic channel\n");
}
mpt1327->tx_sched.state = SCHED_STATE_TC_ADDR;
break;
default:
/* on dirty state (e.g. changing from CC to TC), we enter idle state */
mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
return -1;
}
return 0;
}
int mpt1327_send_codeword(mpt1327_t *mpt1327, uint64_t *bits)
{
mpt1327_codeword_t codeword;
int rc = -1;
memset(&codeword, 0, sizeof(codeword));
switch (mpt1327->dsp_mode) {
case DSP_MODE_CONTROL:
rc = mpt1327_send_codeword_control(mpt1327, &codeword);
break;
case DSP_MODE_TRAFFIC:
rc = mpt1327_send_codeword_traffic(mpt1327, &codeword);
break;
default:
;
}
if (rc < 0)
return 0;
*bits = mpt1327_encode_codeword(&codeword);
return 64;
}
static void out_setup(mpt1327_unit_t *unit, uint8_t network_type, int network_id)
{
char caller_id[32], id[16];
/* setup call */
PDEBUG(DMPT1327, DEBUG_INFO, "Setup call to network.\n");
sprintf(caller_id, "%03d%04d", unit->prefix, unit->ident);
if (network_id)
sprintf(id, "%d", network_id);
else
id[0] = '\0';
unit->callref = call_up_setup(caller_id, unit->called_number, network_type, id);
}
static void _cancel_pending_call(mpt1327_t *mpt1327, mpt1327_unit_t *unit)
{
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "We are already in a call, the phone might have restarted, so we free old channel first.\n");
mpt1327_go_idle(unit->tc);
timer_stop(&unit->timer);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
}
void mpt1327_receive_codeword_control(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
{
mpt1327_unit_t *unit;
mpt1327_t *tc;
switch (codeword->type) {
case MPT_RQR: /* register */
mpt1327_reset_sync(mpt1327); /* message complete */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) registers\n", unit->prefix, unit->ident);
if (unit->tc)
_cancel_pending_call(mpt1327, unit);
unit_new_state(unit, UNIT_REGISTER_ACK);
break;
case MPT_RQT: /* diversion */
mpt1327_reset_sync(mpt1327); /* message complete */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
unit->called_ident = codeword->params[MPT_IDENT1];
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) requests diversion\n", unit->prefix, unit->ident);
if (unit->tc)
_cancel_pending_call(mpt1327, unit);
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Diversion not supported by TSC, rejecting...\n");
unit_new_state(unit, UNIT_DIVERSION_REJ);
break;
case MPT_RQS: /* simple call */
case MPT_RQE: /* emergency call */
mpt1327_reset_sync(mpt1327); /* message complete */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
unit->called_ident = codeword->params[MPT_IDENT1];
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Ident:%d%s\n", unit->prefix, unit->ident, unit->called_ident, (codeword->type == MPT_RQE) ? " (emergency)" : "");
if (unit->tc)
_cancel_pending_call(mpt1327, unit);
tc = search_free_tc();
if (!tc) {
unit_new_state(unit, UNIT_CALLING_REJ);
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "No free Traffic Channel, call is rejected.\n");
break;
}
if (codeword->params[MPT_EXT]) {
int exchange;
unit->called_type = CALLED_TYPE_PBX_SHORT;
sprintf(unit->called_number, "%d", unit->called_ident);
exchange = ((codeword->params[MPT_FLAG1] << 1) | codeword->params[MPT_FLAG2]) + 1;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PBX exchange %d, Number %s\n", exchange, unit->called_number);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PBX, exchange);
} else if (unit->called_ident >= IDENT_PSTNSI1 && unit->called_ident < IDENT_PSTNSI1 + 15) {
unit->called_type = CALLED_TYPE_PSTN_PRE;
sprintf(unit->called_number, "%d", unit->called_ident - IDENT_PSTNSI1 + 1);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with pre-arranged Number %s\n", unit->called_number);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
} else switch (unit->called_ident) {
case IDENT_IPFIXI:
unit->called_type = CALLED_TYPE_INTERPFX;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to Unit/Group %d with different Prefix\n", unit->called_ident);
unit_new_state(unit, UNIT_CALLING_AHYC);
unit->repeat = REPEAT_AHYC;
break;
case IDENT_ALLI:
unit->called_type = CALLED_TYPE_SYSTEM;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> System wide Call\n");
unit_new_state(unit, UNIT_GTC_P);
unit->repeat = REPEAT_GTC;
break;
case IDENT_PSTNGI:
if (codeword->params[MPT_FLAG1]) {
unit->called_type = CALLED_TYPE_PSTN_LONG2;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with long Number (10..31 Digits)\n");
} else {
unit->called_type = CALLED_TYPE_PSTN_LONG1;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with long Number (1..9 Digits)\n");
}
unit_new_state(unit, UNIT_CALLING_AHYC);
unit->repeat = REPEAT_AHYC;
break;
case IDENT_PABXI:
unit->called_type = CALLED_TYPE_PBX_LONG;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PBX (long number)\n");
unit_new_state(unit, UNIT_CALLING_AHYC);
unit->repeat = REPEAT_AHYC;
break;
default:
unit->called_type = CALLED_TYPE_UNIT;
unit->called_prefix = unit->prefix;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to Unit/Group %d (same Prefix)\n", unit->called_ident);
unit_new_state(unit, UNIT_GTC_P);
unit->repeat = REPEAT_GTC;
}
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Allocating Traffic Channel %s\n", tc->sender.kanal);
mpt1327_new_state(tc, STATE_BUSY, unit);
break;
case MPT_SAMIS: /* SAMIS response */
unit = get_unit(mpt1327->rx_sched.data_prefix, mpt1327->rx_sched.data_ident);
if (unit->state != UNIT_CALLING_SAMIS) {
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends SAMIS, but not requested\n", unit->prefix, unit->ident);
break;
}
switch (unit->called_type) {
case CALLED_TYPE_INTERPFX:
if (codeword->params[MPT_DESC] != 0x0) {
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x0, (int)codeword->params[MPT_DESC]);
return;
}
unit->called_prefix = codeword->params[MPT_PARAMETERS1] >> 13;
unit->called_ident = codeword->params[MPT_PARAMETERS1] & 0x1fff;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Prefix:%d Ident:%d\n", unit->prefix, unit->ident, unit->called_prefix, unit->called_ident);
unit_new_state(unit, UNIT_GTC_B);
unit->repeat = REPEAT_GTC;
break;
case CALLED_TYPE_PSTN_LONG1:
case CALLED_TYPE_PSTN_LONG2:
if (codeword->params[MPT_DESC] != 0x1) {
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x1, (int)codeword->params[MPT_DESC]);
return;
}
unit->called_number[0] = '0';
unit->called_number[1] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 16) & 0xf];
unit->called_number[2] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 12) & 0xf];
unit->called_number[3] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 8) & 0xf];
unit->called_number[4] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 4) & 0xf];
unit->called_number[5] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 0) & 0xf];
unit->called_number[6] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 12) & 0xf];
unit->called_number[7] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 8) & 0xf];
unit->called_number[8] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 4) & 0xf];
unit->called_number[9] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 0) & 0xf];
unit->called_number[10] = '\0';
/* schedule reception of one or two words */
mpt1327->rx_sched.data_num = codeword->params[MPT_PARAMETERS2] >> 16;
mpt1327->rx_sched.data_count = 0;
mpt1327->rx_sched.data_word = MPT_SAMIS_DT;
if (mpt1327->rx_sched.data_num == 0) {
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
}
break;
case CALLED_TYPE_PBX_LONG:
if (codeword->params[MPT_DESC] != 0x2) {
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x2, (int)codeword->params[MPT_DESC]);
return;
}
unit->called_number[0] = '0';
unit->called_number[1] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 16) & 0xf];
unit->called_number[2] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 12) & 0xf];
unit->called_number[3] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 8) & 0xf];
unit->called_number[4] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 4) & 0xf];
unit->called_number[5] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 0) & 0xf];
unit->called_number[6] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 12) & 0xf];
unit->called_number[7] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 8) & 0xf];
unit->called_number[8] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 4) & 0xf];
unit->called_number[9] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 0) & 0xf];
unit->called_number[10] = '\0';
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
timer_stop(&unit->timer);
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PBX, 0);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
break;
default:
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Want to receive SAMIS, but called_type not set correctly, please fix!\n");
abort();
}
break;
case MPT_SAMIS_DT:
unit = get_unit(mpt1327->rx_sched.data_prefix, mpt1327->rx_sched.data_ident);
if (unit->state != UNIT_CALLING_SAMIS) {
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends SAMIS, but not requested\n", unit->prefix, unit->ident);
break;
}
if (mpt1327->rx_sched.data_count == 1) {
unit->called_number[10] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 40) & 0xf];
unit->called_number[11] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 36) & 0xf];
unit->called_number[12] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 32) & 0xf];
unit->called_number[13] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 28) & 0xf];
unit->called_number[14] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 24) & 0xf];
unit->called_number[15] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 20) & 0xf];
unit->called_number[16] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 16) & 0xf];
unit->called_number[17] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 12) & 0xf];
unit->called_number[18] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 8) & 0xf];
unit->called_number[19] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 4) & 0xf];
unit->called_number[20] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 0) & 0xf];
unit->called_number[21] = '\0';
if (mpt1327->rx_sched.data_num == 1) {
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
}
} else {
unit->called_number[21] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 40) & 0xf];
unit->called_number[22] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 36) & 0xf];
unit->called_number[23] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 32) & 0xf];
unit->called_number[24] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 28) & 0xf];
unit->called_number[25] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 24) & 0xf];
unit->called_number[26] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 20) & 0xf];
unit->called_number[27] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 16) & 0xf];
unit->called_number[28] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 12) & 0xf];
unit->called_number[29] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 8) & 0xf];
unit->called_number[30] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 4) & 0xf];
unit->called_number[31] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 0) & 0xf];
unit->called_number[32] = '\0';
mpt1327->rx_sched.data_num = 0; /* just in case it is more than 2 data words */
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
unit_new_state(unit, UNIT_GTC_A);
unit->repeat = REPEAT_GTC;
}
break;
case MPT_RQX: /* call cancel */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
unit->called_ident = codeword->params[MPT_IDENT1];
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) cancels call to %d\n", unit->prefix, unit->ident, unit->called_ident);
unit_new_state(unit, UNIT_CANCEL_ACK);
if (unit->tc) {
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Free Traffic Channel %s, because unit cancels on control channel\n", unit->tc->sender.kanal);
mpt1327_go_idle(unit->tc);
}
break;
case MPT_ACKI: /* ack from unit (not ready, wait for RQQ) */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
timer_stop(&unit->timer);
if (unit->state == UNIT_CALLED_ACK) {
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) acknowledges call (not yet ready, waiting for RQQ\n", unit->prefix, unit->ident);
if (unit->callref)
call_up_alerting(unit->callref);
break;
}
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) acknowledges, no call\n", unit->prefix, unit->ident);
break;
case MPT_ACK: /* ack from unit */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
timer_stop(&unit->timer);
if (unit->state == UNIT_CALLED_ACK) {
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) acknowledges call\n", unit->prefix, unit->ident);
answer:
if (unit->callref) {
char connected_id[32];
sprintf(connected_id, "%03d%04d", unit->prefix, unit->ident);
call_up_answer(unit->callref, connected_id);
}
unit_new_state(unit, UNIT_GTC_B);
unit->repeat = REPEAT_GTC;
break;
}
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) acknowledges, no call\n", unit->prefix, unit->ident);
break;
case MPT_RQQ: /* status from radio */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends RRQ with STATUS=%d\n", unit->prefix, unit->ident, (int)codeword->params[MPT_STATUS]);
switch (codeword->params[MPT_STATUS]) {
case 0x00:
if (unit->state == UNIT_CALLED_ACK) {
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) answers call\n", unit->prefix, unit->ident);
// NOTE: GTC acknowledges RQQ
goto answer;
}
break;
case 0x1f:
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) rejects call, releasing\n", unit->prefix, unit->ident);
// NOTE: AHYX acknowledges RQQ
mpt1327_release(unit);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
break;
}
break;
case MPT_ALH: /* control channel Aloha for loopback mode */
case MPT_ALHS:
case MPT_ALHD:
case MPT_ALHE:
case MPT_ALHR:
case MPT_ALHX:
case MPT_ALHF:
/* schedule reception of CCSC word */
mpt1327->rx_sched.data_num = 1;
mpt1327->rx_sched.data_count = 0;
mpt1327->rx_sched.data_word = MPT_CCSC;
break;
default:
if (mpt1327->sender.loopback)
return;
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Received unsupported codeword '%s' = '%s' on control channel\n", codeword->short_name, codeword->long_name);
}
}
void mpt1327_receive_codeword_traffic(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
{
mpt1327_unit_t *unit;
switch (codeword->type) {
case MPT_MAINT: /* maintenance message */
unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
if (codeword->params[MPT_CHAN] != mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal))) {
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Radio Unit (Prefix:%d Ident:%d) sends maintenance message on wrong channel %d, ignoring!\n", unit->prefix, unit->ident, (int)codeword->params[MPT_CHAN]);
return;
}
if (!unit->tc) {
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Radio Unit (Prefix:%d Ident:%d) sends maintenance, but it has no channel assigned, ignoring!\n", unit->prefix, unit->ident);
return;
}
switch (codeword->params[MPT_OPER]) {
case OPER_PRESSEL_ON:
if (sysdef.timeout)
timer_start(&unit->timer, sysdef.timeout);
mpt1327->pressel_on = 1;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) starts transmission\n", unit->prefix, unit->ident);
break;
case OPER_PRESSEL_OFF:
if (sysdef.timeout)
timer_start(&unit->timer, sysdef.timeout);
mpt1327->pressel_on = 0;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) stops transmission\n", unit->prefix, unit->ident);
break;
case OPER_DISCONNECT:
/* ignore while we send clear message */
if (unit->state == UNIT_CALL_CLEAR)
return;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) disconnects from channel\n", unit->prefix, unit->ident);
if (unit->state == UNIT_CALL) {
timer_stop(&unit->timer);
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Free Traffic Channel %s, because the initiator goes on-hook\n", unit->tc->sender.kanal);
mpt1327_go_idle(unit->tc);
if (unit->callref) {
call_up_release(unit->callref, CAUSE_NORMAL);
unit->callref = 0;
}
}
unit_new_state(unit, UNIT_IDLE);
break;
case OPER_PERIODIC:
if (sysdef.timeout)
timer_start(&unit->timer, sysdef.timeout);
mpt1327->pressel_on = 1;
PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) sends periodic message\n", unit->prefix, unit->ident);
break;
}
break;
default:
if (mpt1327->sender.loopback)
return;
PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Received unsupported codeword '%s' = '%s' on traffic channel\n", codeword->short_name, codeword->long_name);
}
}
void mpt1327_receive_codeword(mpt1327_t *mpt1327, uint64_t bits, double quality, double level)
{
mpt1327_codeword_t codeword;
int rc;
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f%%\n", level * 100.0, quality * 100.0);
rc = mpt1327_decode_codeword(&codeword, (mpt1327->rx_sched.data_num) ? mpt1327->rx_sched.data_word : -1, (mpt1327->sender.loopback) ? MPT_DOWN : MPT_UP, bits);
if (rc < 0) {
mpt1327->rx_sched.data_num = 0;
mpt1327_reset_sync(mpt1327); /* message complete */
return;
}
/* count if we have data words */
if (mpt1327->rx_sched.data_num)
mpt1327->rx_sched.data_count++;
switch (mpt1327->dsp_mode) {
case DSP_MODE_CONTROL:
mpt1327_receive_codeword_control(mpt1327, &codeword);
break;
case DSP_MODE_TRAFFIC:
mpt1327_receive_codeword_traffic(mpt1327, &codeword);
break;
default:
;
}
/* if all data words are received */
if (mpt1327->rx_sched.data_num && mpt1327->rx_sched.data_count == mpt1327->rx_sched.data_num)
mpt1327->rx_sched.data_num = 0;
/* reset receiver unless there is a pending data word to be received */
if (mpt1327->rx_sched.data_num == 0)
mpt1327_reset_sync(mpt1327); /* message complete */
}
void mpt1327_signal_indication(mpt1327_t *mpt1327)
{
/* restart timer, if enabled */
if (mpt1327->unit && mpt1327->unit->state == UNIT_CALL) {
if (sysdef.timeout)
timer_start(&mpt1327->unit->timer, sysdef.timeout);
}
}
/* Timeout handling */
static void mpt1327_timeout(struct timer *timer)
{
mpt1327_t *mpt1327 = (mpt1327_t *)timer->priv;
switch (mpt1327->state) {
default:
break;
}
}
/*
* call control (from upper layer)
*/
/* Call control starts call towards mobile station. */
int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
mpt1327_unit_t *unit;
mpt1327_t *tc;
uint8_t prefix;
uint16_t ident;
/* 1. split number into prefix and ident */
prefix = (dialing[0] - '0') * 100 + (dialing[1] - '0') * 10 + (dialing[2] - '0');
ident = atoi(dialing + 3);
/* 2. check if given number is already in a call, return BUSY */
unit = get_unit(prefix, ident);
if (unit->state != UNIT_IDLE) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call to busy Radio Unit, rejecting!\n");
return -CAUSE_BUSY;
}
/* 3. check if all channels are busy, return NOCHANNEL */
tc = search_free_tc();
if (!tc) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
PDEBUG(DMPT1327, DEBUG_INFO, "Outgoing call to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
/* 4. trying to reach radio unit */
unit->callref = callref;
mpt1327_new_state(tc, STATE_BUSY, unit);
unit_new_state(unit, UNIT_CALLED_AHY);
unit->repeat = REPEAT_AHY;
unit->called_prefix = unit->prefix;
unit->called_ident = unit->ident;
return 0;
}
void call_down_answer(int __attribute__((unused)) callref)
{
}
/* Call control sends disconnect (with tones).
* An active call stays active, so tones and annoucements can be received
* by mobile station.
*/
void call_down_disconnect(int callref, int cause)
{
mpt1327_unit_t *unit;
PDEBUG(DMPT1327, DEBUG_INFO, "Call has been disconnected by network.\n");
unit = find_unit_callref(callref);
if (!unit) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing disconnect, but no unit for callref!\n");
call_up_release(callref, CAUSE_INVALCALLREF);
return;
}
/* Release when not active */
if (unit->state == UNIT_CALL)
return;
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing disconnect, but no call, releasing!\n");
mpt1327_release(unit);
unit->callref = 0;
call_up_release(callref, cause);
}
/* Call control releases call toward mobile station. */
void call_down_release(int callref, __attribute__((unused)) int cause)
{
mpt1327_unit_t *unit;
PDEBUG(DMPT1327, DEBUG_INFO, "Call has been released by network, releasing call.\n");
unit = find_unit_callref(callref);
if (!unit) {
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing release, but no unit for callref!\n");
/* don't send release, because caller already released */
return;
}
PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing release, releasing!\n");
mpt1327_release(unit);
unit->callref = 0;
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
{
mpt1327_unit_t *unit;
unit = find_unit_callref(callref);
if (!unit)
return;
if (!unit->tc)
return;
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC) {
sample_t up[(int)((double)count * unit->tc->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&unit->tc->sender.srstate, samples, count, up);
jitter_save(&unit->tc->sender.dejitter, up, count);
}
}
void dump_info(void)
{
dump_units();
}
void call_down_clock(void) {}