You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1144 lines
29 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015, 2016, 2017
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/>.
*/
#define _GNU_SOURCE
#include <dml/dml_client.h>
#include <dml/dml_connection.h>
#include <dml/dml_packet.h>
#include <dml/dml.h>
#include <dml/dml_host.h>
#include <dml/dml_id.h>
#include <dml/dml_crypto.h>
#include "dml_config.h"
#include <dml/dml_stream.h>
#include "fprs_db.h"
#include "fprs_parse.h"
#include "trx_dv.h"
#include "soundlib.h"
#include <eth_ar/eth_ar.h>
#include <eth_ar/fprs.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define RXSTATE_CHECK_TIMER_NS 100000000
#define DML_TRX_DATA_KEEPALIVE 10
#define DML_TRX_FPRS_TIMER (10 * 60)
//#define DML_TRX_FPRS_TIMER (1 * 60)
#define DML_TRX_FPRS_TIMER_INIT (10)
#define DML_TRX_FPRS_DB_TIMER 10
#define TIME_VALID_UPLINK (1*60)
#define TIME_VALID_DOWNLINK (5*60)
#define TIME_VALID_OWN (60*60)
#define DML_TRX_LEVEL_MSG 255
#define debug(...) printf(__VA_ARGS__)
static bool fullduplex = false;
static bool digipeater = false;
static bool allow_commands = true;
static struct dml_stream *stream_dv;
static struct dml_stream *stream_fprs;
static struct dml_host *host;
/* Stream we are connected to */
static struct dml_stream *cur_con = NULL;
static struct dml_stream *cur_db = NULL;
/* Who didn't like us */
static struct dml_stream *last_db_req_disc = NULL;
static int send_data_fprs(void *data, size_t size, unsigned int link, void *arg);
static void recv_data(void *data, size_t size);
static void recv_data_fprs(void *data, size_t size, uint64_t timestamp);
static uint8_t rx_state = false;
static uint8_t tx_state = false;
static char command[100];
static int command_len = 0;
static char command_pipe[100];
static int command_pipe_len = 0;
static uint8_t mac_last[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static uint8_t mac_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static uint8_t mac_dev[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static double my_fprs_longitude = 0.0;
static double my_fprs_latitude = 0.0;
static char *my_fprs_text = "";
static char my_call[ETH_AR_CALL_SIZE];
static char *message_connect;
static char *message_disconnect;
static char *message_remote_disconnect;
static char *message_remote_disconnect_400;
static char *message_remote_disconnect_401;
static char *message_remote_disconnect_503;
static char *message_notfound;
static char *message_notallowed;
static uint8_t *header;
enum sound_msg {
SOUND_MSG_SILENCE,
SOUND_MSG_CONNECT,
SOUND_MSG_DISCONNECT,
SOUND_MSG_REMOTE_DISC,
SOUND_MSG_NOTFOUND,
SOUND_MSG_NOTALLOWED,
SOUND_MSG_HEADER,
};
struct sound_msg_e {
struct sound_msg_e *next;
uint8_t *data;
size_t size;
bool free_data;
};
static struct sound_msg_e *sound_msg_q = NULL;
static char *command_prefix = "";
static void queue_sound_msg(enum sound_msg msg)
{
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
struct sound_msg_e **q = &sound_msg_q;
if (!ent)
goto err_ent;
uint8_t *data;
size_t size;
data = soundlib_get(msg, &size);
if (!data)
goto err_data;
ent->free_data = false;
ent->size = size;
ent->data = data;
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_data:
free(ent);
err_ent:
return;
}
static void queue_sound_spell(char *text)
{
size_t size;
uint8_t *data = soundlib_spell(text, &size);
struct sound_msg_e **q = &sound_msg_q;
if (!data)
return;
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
if (!ent)
goto err_ent;
ent->data = data;
ent->size = size;
ent->free_data = true;
printf("Queue: %p %zd\n", data, size);
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_ent:
free(data);
return;
}
static void queue_sound_synthesize(char *text)
{
size_t size;
uint8_t *data = soundlib_synthesize(text, &size);
struct sound_msg_e **q = &sound_msg_q;
if (!data)
return;
struct sound_msg_e *ent = calloc(sizeof(struct sound_msg_e), 1);
if (!ent)
goto err_ent;
ent->data = data;
ent->size = size;
ent->free_data = true;
printf("Queue: %p %zd\n", data, size);
while (*q)
q = &(*q)->next;
*q = ent;
return;
err_ent:
free(data);
return;
}
static int send_data(void *data, size_t size, void *sender_arg)
{
struct dml_stream *sender = sender_arg;
struct dml_crypto_key *dk = dml_stream_crypto_get(sender);
uint64_t timestamp;
struct timespec ts;
uint16_t packet_id = dml_stream_data_id_get(sender);
struct dml_connection *con = dml_host_connection_get(host);
if (!packet_id)
return -1;
clock_gettime(CLOCK_REALTIME, &ts);
timestamp = dml_ts2timestamp(&ts);
if (con)
dml_packet_send_data(con, packet_id, data, size, timestamp, dk);
return 0;
}
static int fprs_update_status(char *stream, char *assoc)
{
struct fprs_frame *fprs_frame;
uint8_t dml_data[1024];
size_t dml_size = 1024;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
fprs_frame = fprs_frame_create();
if (!fprs_frame)
return -1;
fprs_frame_add_dmlstream(fprs_frame, stream);
fprs_frame_add_dmlassoc(fprs_frame, assoc);
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
/* Send FPRS frame with callsign in FreeDV header */
trx_dv_send_fprs(mac_dev, mac_bcast, dml_data, dml_size);
/* Add callsign to packet for others */
fprs_frame_add_callsign(fprs_frame, mac_dev);
dml_size = sizeof(dml_data);
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
fprs_parse_data(dml_data, dml_size, &ts,
FPRS_PARSE_DOWNLINK,
TIME_VALID_OWN,
send_data_fprs,
NULL
);
fprs_frame_destroy(fprs_frame);
return 0;
}
static int fprs_update_mac(uint8_t mac[6])
{
struct fprs_frame *fprs_frame;
uint8_t dml_data[1024];
size_t dml_size = 1024;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
/* Only if it is from someone else */
if (!memcmp(mac, mac_dev, 6))
return 0;
/* Only if we know who send something */
if (!memcmp(mac, mac_bcast, 6))
return 0;
fprs_frame = fprs_frame_create();
if (!fprs_frame)
return -1;
fprs_frame_add_callsign(fprs_frame, mac);
fprs_frame_add_dmlassoc(fprs_frame, dml_stream_name_get(stream_dv));
dml_size = sizeof(dml_data);
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
fprs_parse_data(dml_data, dml_size, &ts,
FPRS_PARSE_DOWNLINK,
TIME_VALID_DOWNLINK,
send_data_fprs,
NULL
);
fprs_frame_destroy(fprs_frame);
return 0;
}
static gboolean fprs_timer(void *arg)
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
debug("fprs_timer elapsed\n");
fprs_db_flush(ts.tv_sec);
fprs_update_status(dml_stream_name_get(stream_dv),
cur_con ? dml_stream_name_get(cur_con) : "");
if (my_fprs_longitude != 0.0 ||
my_fprs_latitude != 0.0) {
struct fprs_frame *fprs_frame;
uint8_t dml_data[1024];
size_t dml_size = 1024;
fprs_frame = fprs_frame_create();
if (!fprs_frame)
return -1;
fprs_frame_add_position(fprs_frame, my_fprs_longitude, my_fprs_latitude, true);
fprs_frame_add_symbol(fprs_frame, (uint8_t[2]){'F','&'});
if (my_fprs_text && strlen(my_fprs_text))
fprs_frame_add_comment(fprs_frame, my_fprs_text);
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
trx_dv_send_fprs(mac_dev, mac_bcast, dml_data, dml_size);
fprs_frame_add_callsign(fprs_frame, mac_dev);
dml_size = sizeof(dml_data);
fprs_frame_data_get(fprs_frame, dml_data, &dml_size);
fprs_parse_data(dml_data, dml_size, &ts,
FPRS_PARSE_DOWNLINK,
TIME_VALID_OWN,
send_data_fprs,
NULL
);
fprs_frame_destroy(fprs_frame);
}
struct dml_connection *con = dml_host_connection_get(host);
if (cur_db && con) {
dml_packet_send_connect(con,
dml_stream_id_get(cur_db),
dml_stream_data_id_get(cur_db));
dml_packet_send_req_reverse(con, dml_stream_id_get(cur_db),
dml_stream_id_get(stream_fprs),
DML_PACKET_REQ_REVERSE_CONNECT,
DML_STATUS_OK);
}
g_timeout_add_seconds(DML_TRX_FPRS_TIMER, fprs_timer, &fprs_timer);
return G_SOURCE_REMOVE;
}
static gboolean fprs_db_check(void *arg)
{
if (!cur_db) {
struct dml_stream *ds = NULL;
while ((ds = dml_stream_iterate(ds))) {
char *mime = dml_stream_mime_get(ds);
char *alias = dml_stream_alias_get(ds);
if (mime && !strcmp(DML_MIME_FPRS, mime) &&
alias && !strcmp(DML_ALIAS_FPRS_DB, alias) &&
ds != last_db_req_disc &&
!cur_db) {
struct dml_crypto_key *ck = dml_stream_crypto_get(ds);
if (ck) {
cur_db = ds;
break;
}
}
}
if (cur_db) {
if (dml_host_connect(host, cur_db))
cur_db = NULL;
else
dml_packet_send_req_reverse(dml_host_connection_get(host), dml_stream_id_get(cur_db),
dml_stream_id_get(stream_fprs),
DML_PACKET_REQ_REVERSE_CONNECT,
DML_STATUS_OK);
}
} else {
fprs_parse_request_flush(send_data_fprs, NULL);
}
g_timeout_add_seconds(DML_TRX_FPRS_DB_TIMER, fprs_db_check, &cur_db);
return G_SOURCE_REMOVE;
}
static void stream_removed_cb(struct dml_host *host, struct dml_stream *ds, void *arg)
{
if (ds == cur_con) {
cur_con = NULL;
fprs_update_status(
dml_stream_name_get(stream_dv), "");
}
if (ds == cur_db) {
cur_db = NULL;
}
}
static void stream_data_cb(struct dml_host *host, struct dml_stream *ds, uint64_t timestamp, void *data, size_t data_size, void *arg)
{
if (ds != cur_con && ds != cur_db) {
fprintf(stderr, "Received spurious data from %s\n", dml_stream_name_get(ds));
return;
}
if (ds == cur_con) {
fprintf(stderr, "Received %zd ok\n", data_size);
recv_data(data, data_size);
} else {
fprintf(stderr, "Received %zd ok from DB\n", data_size);
recv_data_fprs(data, data_size, timestamp);
}
}
static void stream_header_cb(struct dml_host *host, struct dml_stream *ds, void *header, size_t header_size, void *arg)
{
if (ds != cur_con && ds != cur_db) {
fprintf(stderr, "Received spurious data from %s\n", dml_stream_name_get(ds));
return;
}
fprintf(stderr, "Received header (%zd bytes)\n", header_size);
if (!header_size)
return;
if (ds == cur_con) {
fprintf(stderr, "Play header\n");
trx_dv_send(mac_dev, mac_bcast, 'A', header, header_size, DML_TRX_LEVEL_MSG);
} else {
fprintf(stderr, "Stream mismatch: %p %p\n", ds, cur_con);
}
}
static void stream_req_reverse_connect_cb(struct dml_host *host, struct dml_stream *ds, struct dml_stream *ds_rev, int status, void *arg)
{
bool do_reject = false;
bool do_connect = true;
status = DML_STATUS_OK;
if (cur_con) {
if (cur_con != ds_rev) {
do_reject = true;
status = DML_STATUS_UNAVAILABLE;
}
do_connect = false;
}
if (!dml_host_mime_filter(host, ds_rev)) {
do_connect = false;
do_reject = true;
status = DML_STATUS_BAD;
}
if (do_connect) {
struct dml_crypto_key *key = dml_stream_crypto_get(ds_rev);
if (key) {
printf("Request accepted, connecting\n");
dml_packet_send_req_header(dml_host_connection_get(host), dml_stream_id_get(ds_rev));
dml_host_connect(host, ds_rev);
cur_con = ds_rev;
fprs_update_status(dml_stream_name_get(stream_dv), dml_stream_name_get(cur_con));
if (message_connect)
queue_sound_synthesize(message_connect);
else
queue_sound_msg(SOUND_MSG_CONNECT);
} else {
printf("No valid crypto key for this stream (yet)\n");
do_reject = true;
status = DML_STATUS_UNAUTHORIZED;
}
}
if (do_reject) {
printf("Request rejected\n");
dml_packet_send_req_reverse(dml_host_connection_get(host),
dml_stream_id_get(ds_rev),
dml_stream_id_get(ds),
DML_PACKET_REQ_REVERSE_DISC,
status);
}
}
static void stream_req_reverse_disconnect_cb(struct dml_host *host, struct dml_stream *ds, struct dml_stream *ds_rev, int status, void *arg)
{
if (ds_rev == cur_con) {
printf("Disconnect\n");
dml_packet_send_req_disc(dml_host_connection_get(host), dml_stream_id_get(ds_rev));
cur_con = NULL;
fprs_update_status(
dml_stream_name_get(stream_dv), "");
char *synth_msg;
switch (status) {
case DML_STATUS_BAD:
synth_msg = message_remote_disconnect_400;
break;
case DML_STATUS_UNAUTHORIZED:
synth_msg = message_remote_disconnect_401;
break;
case DML_STATUS_UNAVAILABLE:
synth_msg = message_remote_disconnect_503;
break;
case DML_STATUS_OK:
default:
synth_msg = message_remote_disconnect;
}
if (synth_msg)
queue_sound_synthesize(synth_msg);
else
queue_sound_msg(SOUND_MSG_REMOTE_DISC);
}
if (ds_rev == cur_db) {
printf("DB requests disconnect\n");
dml_packet_send_req_disc(dml_host_connection_get(host), dml_stream_id_get(ds_rev));
cur_db = NULL;
last_db_req_disc = ds_rev;
}
}
static void connection_closed_cb(struct dml_host *host, void *arg)
{
cur_con = NULL;
cur_db = NULL;
}
static int send_data_fprs(void *data, size_t size, unsigned int link, void *arg)
{
int r = 0;
if (link & FPRS_PARSE_DOWNLINK)
r |= trx_dv_send_fprs(mac_dev, mac_bcast, data, size);
if (link & FPRS_PARSE_UPLINK)
r |= send_data(data, size, stream_fprs);
return r;
}
static void recv_data_fprs(void *data, size_t size, uint64_t timestamp)
{
struct timespec ts;
dml_timestamp2ts(&ts, timestamp);
fprs_parse_data(data, size, &ts,
FPRS_PARSE_UPLINK,
TIME_VALID_UPLINK,
send_data_fprs,
NULL
);
}
static void recv_data(void *data, size_t size)
{
if (size < 8)
return;
uint8_t *datab = data;
uint8_t mode = datab[6];
uint8_t state = datab[7];
// printf("mode %d state %d\n", mode, state);
if (!rx_state || fullduplex) {
if (state != tx_state) {
char call[ETH_AR_CALL_SIZE];
int ssid;
bool multicast;
eth_ar_mac2call(call, &ssid, &multicast, data);
tx_state = state;
printf("State changed to %s by %s-%d\n", state ? "ON":"OFF", multicast ? "MULTICAST" : call, ssid);
}
if (size > 8) {
trx_dv_send(data, mac_bcast, mode, datab + 8, size - 8, state);
}
}
}
static gboolean rx_watchdog(void *arg)
{
printf("No activity, sending state off packet\n");
uint8_t data[8];
memcpy(data, rx_state ? mac_last : mac_bcast, 6);
data[6] = 0;
data[7] = 0;
send_data(data, 8, stream_dv);
rx_state = 0;
/* Flush command buffer */
command_len = 0;
while (sound_msg_q) {
uint8_t *data;
size_t size;
data = soundlib_get(SOUND_MSG_SILENCE, &size);
if (data) {
trx_dv_send(mac_dev, mac_bcast, 'A', data, size, DML_TRX_LEVEL_MSG);
trx_dv_send(mac_dev, mac_bcast, 'A', data, size, DML_TRX_LEVEL_MSG);
trx_dv_send(mac_dev, mac_bcast, 'A', data, size, DML_TRX_LEVEL_MSG);
trx_dv_send(mac_dev, mac_bcast, 'A', data, size, DML_TRX_LEVEL_MSG);
}
struct sound_msg_e *e = sound_msg_q;
data = e->data;
size = e->size;
while (size) {
size_t sendsize = 160;
if (size < sendsize)
sendsize = size;
trx_dv_send(mac_dev, mac_bcast, 'A', data, sendsize, DML_TRX_LEVEL_MSG);
data += sendsize;
size -= sendsize;
}
if (e->free_data)
free(e->data);
sound_msg_q = e->next;
free(e);
}
g_timeout_add_seconds(DML_TRX_DATA_KEEPALIVE, rx_watchdog, &rx_state);
return G_SOURCE_REMOVE;
}
static int dv_in_cb(void *arg, uint8_t from[6], uint8_t to[6], uint8_t *dv, size_t size, int mode, uint8_t level)
{
uint8_t data[8 + size];
if (!rx_state) {
printf("rx_state to on, level: %d\n", level);
}
rx_state = level;
memcpy(data, from, 6);
memcpy(mac_last, from, 6);
data[6] = mode;
data[7] = rx_state;
memcpy(data + 8, dv, size);
send_data(data, 8 + size, stream_dv);
fprs_update_mac(from);
g_source_remove_by_user_data(&rx_state);
if (rx_state)
g_timeout_add(RXSTATE_CHECK_TIMER_NS/1000000, rx_watchdog, &rx_state);
else
g_timeout_add_seconds(DML_TRX_DATA_KEEPALIVE, rx_watchdog, &rx_state);
return 0;
}
static void command_cb_handle(char *command)
{
struct dml_stream *ds = NULL;
bool is_73;
bool do_disconnect = false;
bool do_connect = false;
bool nokey = false;
bool notfound = false;
struct dml_connection *con = dml_host_connection_get(host);
/* Skip empty commands */
if (!strlen(command))
return;
printf("command: %s\n", command);
is_73 = !strcmp(command, "73");
do_disconnect |= is_73;
/* try to find by alias directly */
ds = dml_stream_by_alias(command);
if (!ds) {
/* Second attempt: try to find with added prefix */
char *command_pref;
asprintf(&command_pref, "%s%s", command_prefix, command);
ds = dml_stream_by_alias(command_pref);
free(command_pref);
}
if (!ds && !is_73)
notfound = true;
if (ds && dml_stream_mine_get(ds))
ds = NULL;
if (ds && !is_73) {
struct dml_crypto_key *key = dml_stream_crypto_get(ds);
printf("match_mime: %d, key: %p\n", dml_host_mime_filter(host, ds), key);
if (ds != cur_con && dml_host_mime_filter(host, ds)) {
if (key) {
do_disconnect = true;
do_connect = true;
} else {
nokey = true;
}
}
}
printf("connect: %d disconnect: %d %s %s\n", do_connect, do_disconnect,
ds ? dml_stream_name_get(ds) : "UNKNOWN",
cur_con ? dml_stream_name_get(cur_con) : "NONE");
if (do_disconnect && cur_con) {
if (con) {
dml_packet_send_req_disc(con, dml_stream_id_get(cur_con));
dml_packet_send_req_reverse(con, dml_stream_id_get(cur_con),
dml_stream_id_get(stream_dv),
DML_PACKET_REQ_REVERSE_DISC,
DML_STATUS_OK);
}
cur_con = NULL;
fprs_update_status(dml_stream_name_get(stream_dv), "");
}
if (do_connect) {
if (con) {
dml_packet_send_req_header(con, dml_stream_id_get(ds));
dml_host_connect(host, ds);
}
cur_con = ds;
fprs_update_status(dml_stream_name_get(stream_dv), dml_stream_name_get(cur_con));
dml_packet_send_req_reverse(con, dml_stream_id_get(ds),
dml_stream_id_get(stream_dv),
DML_PACKET_REQ_REVERSE_CONNECT,
DML_STATUS_OK);
if (message_connect)
queue_sound_synthesize(message_connect);
else
queue_sound_msg(SOUND_MSG_CONNECT);
queue_sound_spell(command);
char *constr;
if (asprintf(&constr, "Connecting %s", command) >= 0) {
trx_dv_send_control(mac_dev, mac_bcast, constr);
free(constr);
}
} else {
if (notfound) {
if (message_notfound)
queue_sound_synthesize(message_notfound);
else
queue_sound_msg(SOUND_MSG_NOTFOUND);
queue_sound_spell(command);
} else if (nokey) {
if (message_notallowed)
queue_sound_synthesize(message_notallowed);
else
queue_sound_msg(SOUND_MSG_NOTALLOWED);
queue_sound_spell(command);
} else if (do_disconnect) {
if (message_disconnect)
queue_sound_synthesize(message_disconnect);
else
queue_sound_msg(SOUND_MSG_DISCONNECT);
}
trx_dv_send_control(mac_dev, mac_bcast, "NACK");
}
}
static int command_cb(void *arg, uint8_t from[6], uint8_t to[6], char *ctrl, size_t size)
{
for (; size; size--, ctrl++) {
if (!command_len && ctrl[0] != '*')
continue;
/* Star means start of a new command */
if (ctrl[0] == '*') {
command[0] = '*';
command_len = 1;
continue;
}
command[command_len] = ctrl[0];
command[command_len+1] = 0;
if (command[command_len] == '#') {
if (command[0] == '*') {
command[command_len] = 0;
if (allow_commands)
command_cb_handle(command+1);
}
command_len = 0;
} else {
command_len++;
}
if (command_len >= sizeof(command))
command_len = 0;
}
fprs_update_mac(from);
return 0;
}
static gboolean command_pipe_cb(GIOChannel *source, GIOCondition condition, gpointer arg)
{
int fd = *(int*)arg;
static char c;
ssize_t r = read(fd, &c, 1);
if (r == 1) {
if (c == '\r')
return TRUE;
if (c == '\n') {
if (command_pipe_len) {
command_pipe[command_pipe_len] = 0;
if (allow_commands)
command_cb_handle(command_pipe);
command_pipe_len = 0;
return TRUE;
}
}
command_pipe[command_pipe_len] = c;
command_pipe_len++;
if (command_pipe_len >= sizeof(command_pipe))
command_pipe_len = 0;
}
return TRUE;
}
static int fprs_cb(void *arg, uint8_t from[6], uint8_t *fprsdata, size_t size)
{
struct fprs_frame *fprs_frame;
uint8_t f_data[1024];
size_t f_size = 1024;
fprs_frame = fprs_frame_create();
if (!fprs_frame)
return -1;
fprs_frame_data_set(fprs_frame, fprsdata, size);
if (!fprs_frame_element_by_type(fprs_frame, FPRS_CALLSIGN) &&
!fprs_frame_element_by_type(fprs_frame, FPRS_OBJECTNAME))
fprs_frame_add_callsign(fprs_frame, from);
fprs_frame_data_get(fprs_frame, f_data, &f_size);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
if (digipeater) {
/* Digipeat the incomming FPRS packet */
trx_dv_send_fprs(mac_dev, mac_bcast, f_data, f_size);
}
fprs_parse_data(f_data, f_size, &ts,
FPRS_PARSE_DOWNLINK,
TIME_VALID_DOWNLINK,
send_data_fprs,
NULL
);
fprs_frame_destroy(fprs_frame);
fprs_update_mac(from);
return 0;
}
void mac_dev_cb(uint8_t mac[6])
{
int ssid;
bool multicast;
memcpy(mac_dev, mac, 6);
if (header) {
memcpy(header, mac, 6);
}
eth_ar_mac2call(my_call, &ssid, &multicast, mac_dev);
printf("Interface address %02x:%02x:%02x:%02x:%02x:%02x %s-%d\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
multicast ? "MULTICAST" : my_call, ssid);
}
static char prev_msg[256] = {0};
static char prev_id[256] = {0};
static uint8_t prev_from[6] = {0};
int message_cb(uint8_t to[6], uint8_t from[6],
void *data, size_t dsize, void *id, size_t isize, void *arg)
{
int ssid;
bool multicast;
char from_call[ETH_AR_CALL_SIZE];
char msg_asc[dsize + 1];
char id_asc[isize + 1];
if (memcmp(to, mac_dev, 6))
return -1;
memcpy(msg_asc, data, dsize);
msg_asc[dsize] = 0;
if (id) {
memcpy(id_asc, id, isize);
id_asc[isize] = 0;
} else
id_asc[0] = 0;
if (!memcmp(prev_from, from, 6)) {
if (!strcmp(prev_msg, msg_asc))
return 0;
if (id_asc[0] && !strcmp(id_asc, prev_id))
return 0;
}
strncpy(prev_msg, msg_asc, 255);
strncpy(prev_id, id_asc, 255);
memcpy(prev_from, from, 6);
eth_ar_mac2call(from_call, &ssid, &multicast, from);
printf("Message from %s: %s, ID: %s\n", from_call, msg_asc, id_asc);
queue_sound_synthesize("Message from:");
queue_sound_spell(from_call);
queue_sound_synthesize(msg_asc);
return 0;
}
int main(int argc, char **argv)
{
char *file = "dml_trx.conf";
char *certificate;
char *key;
char *dv_dev;
char *name;
char *description;
char *alias;
char *command_pipe_name;
static uint8_t id[DML_ID_SIZE];
uint32_t bps = 6400;
struct dml_crypto_key *dk;
int fd_command;
GIOChannel *io_command = NULL;
if (argc > 1)
file = argv[1];
host = dml_host_create(file);
if (!host) {
printf("Could not create host\n");
return -1;
}
name = dml_config_value("name", NULL, "test_trx");
alias = dml_config_value("alias", NULL, "0000");
description = dml_config_value("description", NULL, "Test transceiver");
certificate = dml_config_value("certificate", NULL, "");
key = dml_config_value("key", NULL, "");
fullduplex = atoi(dml_config_value("fullduplex", NULL, "0"));
digipeater = atoi(dml_config_value("digipeater", NULL, "0"));
allow_commands = atoi(dml_config_value("allow_commands", NULL, "0"));
command_pipe_name = dml_config_value("command_pipe_name", NULL, NULL);
my_fprs_longitude = atof(dml_config_value("longitude", NULL, "0.0"));
my_fprs_latitude = atof(dml_config_value("latitude", NULL, "0.0"));
my_fprs_text = dml_config_value("fprs_text", NULL, "");
command_prefix = dml_config_value("command_prefix", NULL, "");
dv_dev = dml_config_value("dv_device", NULL, NULL);
if (dv_dev) {
if (trx_dv_init(dv_dev, dv_in_cb, command_cb, fprs_cb, NULL, mac_dev_cb))
fprintf(stderr, "Could not open DV device\n");
} else {
fprintf(stderr, "No DV device configured\n");
return -1;
}
if (dml_crypto_load_cert(certificate)) {
printf("Could not load certificate\n");
return -1;
}
if (!(dk = dml_crypto_private_load(key))) {
printf("Could not load key\n");
return -1;
}
if (dml_id_gen(id, DML_PACKET_DESCRIPTION_VERSION_0, bps,
DML_MIME_DV_C2, name, alias, description))
return -1;
stream_dv = dml_stream_by_id_alloc(id);
dml_stream_mine_set(stream_dv, true);
dml_stream_crypto_set(stream_dv, dk);
dml_stream_name_set(stream_dv, name);
dml_stream_alias_set(stream_dv, alias);
dml_stream_mime_set(stream_dv, DML_MIME_DV_C2);
dml_stream_description_set(stream_dv, description);
dml_stream_bps_set(stream_dv, bps);
if (dml_id_gen(id, DML_PACKET_DESCRIPTION_VERSION_0, bps,
DML_MIME_FPRS, name, "", description))
return -1;
stream_fprs = dml_stream_by_id_alloc(id);
dml_stream_mine_set(stream_fprs, true);
dml_stream_crypto_set(stream_fprs, dk);
dml_stream_name_set(stream_fprs, name);
dml_stream_alias_set(stream_fprs, "");
dml_stream_mime_set(stream_fprs, DML_MIME_FPRS);
dml_stream_description_set(stream_fprs, description);
dml_stream_bps_set(stream_fprs, bps);
dml_host_connection_closed_cb_set(host, connection_closed_cb, NULL);
dml_host_mime_filter_set(host, 2, (char*[]){ DML_MIME_DV_C2 , DML_MIME_FPRS });
dml_host_stream_removed_cb_set(host, stream_removed_cb, NULL);
dml_host_stream_header_cb_set(host, stream_header_cb, NULL);
dml_host_stream_data_cb_set(host, stream_data_cb, NULL);
dml_host_stream_req_reverse_connect_cb_set(host, stream_req_reverse_connect_cb, NULL);
dml_host_stream_req_reverse_disconnect_cb_set(host, stream_req_reverse_disconnect_cb, NULL);
fprs_parse_hook_message(message_cb, NULL);
char *soundlib_voice = dml_config_value("soundlib_voice", NULL, NULL);
if (soundlib_init(8000, soundlib_voice)) {
printf("Could not init soundlib\n");
return -1;
}
soundlib_add_silence(SOUND_MSG_SILENCE, 0.16);
soundlib_add_beep(SOUND_MSG_CONNECT, 800, 0.08);
soundlib_add_beep(SOUND_MSG_DISCONNECT, 1600, 0.16);
soundlib_add_beep(SOUND_MSG_REMOTE_DISC, 2000, 0.16);
soundlib_add_beep(SOUND_MSG_NOTFOUND, 1800, 0.16);
soundlib_add_beep(SOUND_MSG_NOTALLOWED, 2400, 0.16);
char *soundlib_connect = dml_config_value("soundlib_connect", NULL, NULL);
if (soundlib_connect)
soundlib_add_file(SOUND_MSG_CONNECT, soundlib_connect);
char *soundlib_disconnect = dml_config_value("soundlib_disconnect", NULL, NULL);
if (soundlib_disconnect)
soundlib_add_file(SOUND_MSG_DISCONNECT, soundlib_disconnect);
char *soundlib_remote_disc = dml_config_value("soundlib_remote_disc", NULL, NULL);
if (soundlib_remote_disc)
soundlib_add_file(SOUND_MSG_REMOTE_DISC, soundlib_remote_disc);
char *soundlib_notfound = dml_config_value("soundlib_notfound", NULL, NULL);
if (soundlib_notfound)
soundlib_add_file(SOUND_MSG_NOTFOUND, soundlib_notfound);
char *soundlib_notallowed = dml_config_value("soundlib_notallowed", NULL, NULL);
if (soundlib_notallowed)
soundlib_add_file(SOUND_MSG_NOTALLOWED, soundlib_notallowed);
char *soundlib_header = dml_config_value("soundlib_header", NULL, NULL);
if (soundlib_header) {
soundlib_add_file(SOUND_MSG_HEADER, soundlib_header);
size_t header_size;
uint8_t *sl_header = soundlib_get(SOUND_MSG_HEADER, &header_size);
if (sl_header) {
header = calloc(1, header_size + 8);
memcpy(header + 8, sl_header, header_size);
memcpy(header, mac_dev, 6);
header[6] = 'A';
header[7] = 255;
dml_stream_header_set(stream_dv, header, header_size + 8);
}
}
message_connect = dml_config_value("message_connect", NULL, NULL);
message_disconnect = dml_config_value("message_disconnect", NULL, NULL);
message_remote_disconnect = dml_config_value("message_remote_disconnect", NULL, NULL);
message_remote_disconnect_400 = dml_config_value("message_remote_disconnect_400", NULL, NULL);
message_remote_disconnect_401 = dml_config_value("message_remote_disconnect_401", NULL, NULL);
message_remote_disconnect_503 = dml_config_value("message_remote_disconnect_503", NULL, NULL);
message_notfound = dml_config_value("message_notfound", NULL, NULL);
message_notallowed = dml_config_value("message_notallowed", NULL, NULL);
if (command_pipe_name) {
printf("Create command pipe at %s\n", command_pipe_name);
remove(command_pipe_name);
if (mkfifo(command_pipe_name, S_IRUSR | S_IWUSR | S_IWGRP)) {
printf("Could not create command pipe\n");
return -1;
}
fd_command = open(command_pipe_name, O_RDWR | O_NONBLOCK);
if (fd_command < 0) {
printf("Could not open command pipe\n");
return -1;
}
}
io_command = g_io_channel_unix_new(fd_command);
g_io_channel_set_encoding(io_command, NULL, NULL);
g_io_add_watch(io_command, G_IO_IN, command_pipe_cb, &fd_command);
g_timeout_add_seconds(DML_TRX_DATA_KEEPALIVE, rx_watchdog, &rx_state);
g_timeout_add_seconds(DML_TRX_FPRS_TIMER_INIT, fprs_timer, &fprs_timer);
g_timeout_add_seconds(DML_TRX_FPRS_DB_TIMER, fprs_db_check, &cur_db);
g_main_loop_run(g_main_loop_new(NULL, false));
g_io_channel_unref(io_command);
return 0;
}