2023-09-04 19:06:44 +01:00
|
|
|
/*
|
|
|
|
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
|
|
|
|
|
|
|
|
This file is part of QtSoundModem
|
|
|
|
|
|
|
|
QtSoundModem 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.
|
|
|
|
|
|
|
|
QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
|
|
|
|
|
|
|
|
#include "UZ7HOStuff.h"
|
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
extern char modes_name[modes_count][21];
|
2023-09-04 19:06:44 +01:00
|
|
|
extern int RSID_SABM[4];
|
|
|
|
extern int RSID_UI[4];
|
|
|
|
extern int RSID_SetModem[4];
|
|
|
|
extern int needRSID[4];
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
unit ax25_agw;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses sysutils,classes;
|
|
|
|
|
|
|
|
void AGW_init;
|
|
|
|
void AGW_free;
|
|
|
|
void AGW_add_socket(socket integer);
|
|
|
|
void AGW_del_socket(socket integer);
|
|
|
|
void AGW_send_to_app(socket integer; data string);
|
|
|
|
void AGW_AX25_frame_analiz(snd_ch byte; RX boolean; frame string);
|
|
|
|
void AGW_frame_analiz(Socket Integer; frame string);
|
|
|
|
void AGW_explode_frame(Socket Integer; data string);
|
|
|
|
void AGW_AX25_conn(socket integer; snd_ch, mode byte; path string);
|
|
|
|
void AGW_AX25_disc(socket integer; snd_ch, mode byte; path string);
|
|
|
|
void AGW_AX25_data_in(socket integer; snd_ch,PID byte; path,data string);
|
|
|
|
void AGW_Raw_monitor(snd_ch byte; data string);
|
|
|
|
void AGW_frame_header(AGWPort,DataKind,PID,CallFrom,CallTo string; Len word) string;
|
|
|
|
void AGW_C_Frame(port char; CallFrom,CallTo,Conn_MSG string) string;
|
|
|
|
void erase_zero_ssid(call string) string;
|
|
|
|
void AGW_get_socket(socket integer) integer;
|
|
|
|
void clr_zero(data string) string;
|
|
|
|
|
|
|
|
type TAGWUser = record
|
|
|
|
socket TStringList;
|
|
|
|
AGW_frame_buf TStringList;
|
|
|
|
Monitor TStringList;
|
|
|
|
Monitor_raw TStringList;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame);
|
|
|
|
|
|
|
|
void AGW_frame_analiz(AGWUser * AGW);
|
|
|
|
void AGW_send_to_app(void * socket, string * data);
|
|
|
|
string * make_frame(string * data, Byte * path, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpt, boolean pf, boolean cr);
|
|
|
|
void del_incoming_mycalls_by_sock(void * socket);
|
|
|
|
void del_incoming_mycalls(char * src_call);
|
|
|
|
void send_data_buf(TAX25Port * AX25Sess, int nr);
|
|
|
|
void get_monitor_path(Byte * path, char * mycall, char * corrcall, char * digi);
|
|
|
|
void decode_frame(Byte * frame, int len, Byte * path, string * data,
|
|
|
|
Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id,
|
|
|
|
Byte * rpt, Byte * pf, Byte * cr);
|
|
|
|
|
|
|
|
|
|
|
|
int AGWVersion[2] = {2019, 'B'}; // QTSM Signature
|
|
|
|
|
|
|
|
//ports_info1='1;Port1 with LoopBack Port;
|
|
|
|
|
|
|
|
#define LSB 29
|
|
|
|
#define MSB 30
|
|
|
|
#define MON_ON '1'
|
|
|
|
#define MON_OFF '0'
|
|
|
|
#define MODE_OUR 0
|
|
|
|
#define MODE_OTHER 1
|
|
|
|
#define MODE_RETRY 2
|
|
|
|
|
|
|
|
|
|
|
|
AGWUser ** AGWUsers = NULL; // List of currently connected clients
|
|
|
|
int AGWConCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
struct AGWHeader
|
|
|
|
{
|
|
|
|
int Port;
|
|
|
|
unsigned char DataKind;
|
|
|
|
unsigned char filler2;
|
|
|
|
unsigned char PID;
|
|
|
|
unsigned char filler3;
|
|
|
|
char callfrom[10];
|
|
|
|
char callto[10];
|
|
|
|
int DataLength;
|
|
|
|
int reserved;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define AGWHDDRRLEN sizeof(struct AGWHeader)
|
|
|
|
|
|
|
|
|
|
|
|
struct AGWSocketConnectionInfo
|
|
|
|
{
|
|
|
|
int Number; // Number of record - for AGWConnections display
|
|
|
|
void *socket;
|
|
|
|
// SOCKADDR_IN sin;
|
|
|
|
BOOL SocketActive;
|
|
|
|
BOOL RawFlag;
|
|
|
|
BOOL MonFlag;
|
|
|
|
unsigned char CallSign1[10];
|
|
|
|
unsigned char CallSign2[10];
|
|
|
|
BOOL GotHeader;
|
|
|
|
int MsgDataLength;
|
|
|
|
struct AGWHeader AGWRXHeader;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
AGWUser * AGW_get_socket(void * socket)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
AGWUser * AGW = NULL;
|
|
|
|
|
|
|
|
if (AGWConCount == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < AGWConCount; i++)
|
|
|
|
{
|
|
|
|
if (AGWUsers[i]->socket == socket)
|
|
|
|
{
|
|
|
|
AGW = AGWUsers[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return AGW;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_del_socket(void * socket)
|
|
|
|
{
|
|
|
|
AGWUser * AGW = AGW_get_socket(socket);
|
|
|
|
TAX25Port * AX25Sess = NULL;
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
int port = 0;
|
|
|
|
|
|
|
|
// Close any connections
|
|
|
|
|
|
|
|
for (port = 0; port < 4; port++)
|
|
|
|
{
|
|
|
|
for (i = 0; i < port_num; i++)
|
|
|
|
{
|
|
|
|
AX25Sess = &AX25Port[port][i];
|
|
|
|
|
|
|
|
if (AX25Sess->status != STAT_NO_LINK && AX25Sess->socket == socket)
|
|
|
|
{
|
|
|
|
rst_timer(AX25Sess);
|
|
|
|
set_unlink(AX25Sess, AX25Sess->Path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear registrations
|
|
|
|
|
|
|
|
del_incoming_mycalls_by_sock(socket);
|
|
|
|
|
|
|
|
if (AGW == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Clear(&AGW->AGW_frame_buf);
|
|
|
|
freeString(AGW->data_in);
|
|
|
|
AGW->Monitor = 0;
|
|
|
|
AGW->Monitor_raw = 0;
|
|
|
|
AGW->reportFreqAndModem = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_add_socket(void * socket)
|
|
|
|
{
|
|
|
|
AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t)); // One Client
|
|
|
|
memset(User, 0, sizeof(struct AGWUser_t));
|
|
|
|
|
|
|
|
AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *));
|
|
|
|
|
|
|
|
AGWUsers[AGWConCount++] = User;
|
|
|
|
|
|
|
|
User->data_in = newString();
|
|
|
|
User->socket = socket;
|
|
|
|
|
|
|
|
User->Monitor = 0;
|
|
|
|
User->Monitor_raw = 0;
|
|
|
|
User->reportFreqAndModem = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void agw_free()
|
|
|
|
{
|
|
|
|
// AGWUser.AGW_frame_buf.Free;
|
|
|
|
// AGWUser.Monitor.Free;
|
|
|
|
// AGWUser.Monitor_raw.Free;
|
|
|
|
// AGWUser.socket.Free;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
void erase_zero_ssid(call string) string;
|
|
|
|
var
|
|
|
|
p byte;
|
|
|
|
{
|
|
|
|
p = pos('-0',call);
|
|
|
|
if (p>0 ) delete(call,p,2);
|
|
|
|
result = call;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
string * AGW_frame_header(char AGWPort, char DataKind, UCHAR PID, char * CallFrom, char * CallTo, int Len )
|
|
|
|
{
|
|
|
|
string * Msg = newString();
|
|
|
|
|
|
|
|
struct AGWHeader * Hddr = (struct AGWHeader *)Msg->Data;
|
|
|
|
memset(Hddr, 0, sizeof(struct AGWHeader));
|
|
|
|
|
|
|
|
Hddr->Port = AGWPort;
|
|
|
|
Hddr->DataLength = Len;
|
|
|
|
Hddr->DataKind = DataKind;
|
|
|
|
Hddr->PID = PID;
|
|
|
|
strcpy(Hddr->callfrom, CallFrom);
|
|
|
|
strcpy(Hddr->callto, CallTo);
|
|
|
|
|
|
|
|
Msg->Length = sizeof(struct AGWHeader);
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// AGW to APP frames
|
|
|
|
|
|
|
|
string * AGW_R_Frame()
|
|
|
|
{
|
|
|
|
string * Msg = AGW_frame_header(0, 'R', 0, "", "", 8);
|
|
|
|
|
|
|
|
stringAdd(Msg, (UCHAR *)AGWVersion, 8);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_X_Frame(char * CallFrom, UCHAR reg_call)
|
|
|
|
{
|
|
|
|
string * Msg = AGW_frame_header(0, 'x', 0, CallFrom, "", 1);
|
|
|
|
|
|
|
|
stringAdd(Msg, (UCHAR *)®_call, 1);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
string * AGW_G_Frame()
|
|
|
|
{
|
|
|
|
char Ports[256] = "0;";
|
|
|
|
char portMsg[64];
|
|
|
|
|
|
|
|
string * Msg;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
Ports[0]++;
|
|
|
|
if (soundChannel[i])
|
|
|
|
sprintf(portMsg, "Port%c with SoundCard Ch %c;", Ports[0], 'A' + i);
|
|
|
|
else
|
|
|
|
sprintf(portMsg, "Port%c Disabled;", Ports[0]);
|
|
|
|
|
|
|
|
strcat(Ports, portMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(0, 'G', 0, "", "", strlen(Ports) + 1);
|
|
|
|
|
|
|
|
stringAdd(Msg, (UCHAR *)Ports, strlen(Ports) + 1);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_Gs_Frame(int port, Byte * port_info, int Len)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'g', 0, "", "", Len);
|
|
|
|
stringAdd(Msg, port_info, Len);
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
void AGW_Ys_Frame(port char; frame_outstanding string) string;
|
|
|
|
var
|
|
|
|
DataLen word;
|
|
|
|
{
|
|
|
|
DataLen = 4;
|
|
|
|
result = AGW_frame_header(port,'y','','','',DataLen)+frame_outstanding;
|
|
|
|
};
|
|
|
|
|
|
|
|
/
|
|
|
|
void AGW_Y_Frame(port char; CallFrom,CallTo,frame_outstanding string) string;
|
|
|
|
var
|
|
|
|
DataLen word;
|
|
|
|
{
|
|
|
|
DataLen = 4;
|
|
|
|
result = AGW_frame_header(port,'Y','',CallFrom,CallTo,DataLen)+frame_outstanding;
|
|
|
|
};
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_Y_Frame(int port, char * CallFrom, char *CallTo, int frame_outstanding)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'Y', 0, CallFrom, CallTo, 4);
|
|
|
|
|
|
|
|
stringAdd(Msg, (UCHAR *)&frame_outstanding, 4);
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
void AGW_H_Frame(port char; heard string) string;
|
|
|
|
var
|
|
|
|
DataLen word;
|
|
|
|
{
|
|
|
|
DataLen = length(heard);
|
|
|
|
result = AGW_frame_header(port,'H','','','',DataLen)+heard;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_C_Frame(int port, char * CallFrom, char * CallTo, string * Conn_MSG)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = Conn_MSG->Length;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'C', 240, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, Conn_MSG->Data, Conn_MSG->Length);
|
|
|
|
|
|
|
|
freeString(Conn_MSG);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
string * AGW_Ds_Frame(int port, char * CallFrom, char * CallTo, string * Disc_MSG)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = Disc_MSG->Length;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'd', 240, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, Disc_MSG->Data, Disc_MSG->Length);
|
|
|
|
|
|
|
|
freeString(Disc_MSG);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_D_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = Data->Length;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'D', PID, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, Data->Data, Data->Length);
|
|
|
|
|
|
|
|
freeString(Data);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_I_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = strlen(Monitor);
|
|
|
|
Msg = AGW_frame_header(port, 'I', 0, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, (Byte *)Monitor, DataLen);
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
string * AGW_S_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = strlen(Monitor);
|
|
|
|
Msg = AGW_frame_header(port, 'S', 0, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, (Byte *)Monitor, DataLen);
|
|
|
|
return Msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
string * AGW_U_Frame(int port, char * CallFrom, char * CallTo, char * Monitor)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = strlen(Monitor);
|
|
|
|
Msg = AGW_frame_header(port, 'U', 0, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, (Byte *)Monitor, DataLen);
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string * AGW_T_Frame(int port, char * CallFrom, char * CallTo, char * Data)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = strlen(Data);
|
|
|
|
Msg = AGW_frame_header(port, 'T', 0, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, (Byte *)Data, DataLen);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Raw Monitor
|
|
|
|
string * AGW_K_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data)
|
|
|
|
{
|
|
|
|
string * Msg;
|
|
|
|
int DataLen;
|
|
|
|
|
|
|
|
DataLen = Data->Length;
|
|
|
|
|
|
|
|
Msg = AGW_frame_header(port, 'K', PID, CallFrom, CallTo, DataLen);
|
|
|
|
|
|
|
|
stringAdd(Msg, Data->Data, Data->Length);
|
|
|
|
|
|
|
|
freeString(Data);
|
|
|
|
|
|
|
|
return Msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
// APP to AGW frames
|
|
|
|
|
|
|
|
void on_AGW_P_frame(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
UNUSED(AGW);
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_AGW_X_frame(AGWUser * AGW, char * CallFrom)
|
|
|
|
{
|
|
|
|
Byte reg_call;
|
|
|
|
|
|
|
|
if (add_incoming_mycalls(AGW->socket, CallFrom))
|
|
|
|
reg_call = 1;
|
|
|
|
else
|
|
|
|
reg_call = 0;
|
|
|
|
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_X_Frame(CallFrom, reg_call));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_Xs_frame(char * CallFrom)
|
|
|
|
{
|
|
|
|
del_incoming_mycalls(CallFrom);
|
|
|
|
};
|
|
|
|
|
|
|
|
void on_AGW_G_frame(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_G_Frame());
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_Ms_frame(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
AGW->Monitor ^= 1; // Flip State
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_R_frame(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_R_Frame());
|
|
|
|
}
|
|
|
|
|
|
|
|
int refreshModems = 0;
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
|
|
|
|
{
|
|
|
|
// QTSM with a data field is used by QtSM to set/read Modem Params
|
|
|
|
|
2023-12-17 13:53:32 +00:00
|
|
|
Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
|
2023-09-04 19:06:44 +01:00
|
|
|
int Len = 12;
|
|
|
|
|
|
|
|
if (Frame->DataLength == 32)
|
|
|
|
{
|
|
|
|
// BPQ to QTSM private Format.
|
|
|
|
|
2023-12-17 13:53:32 +00:00
|
|
|
// First 4 Freq, 4 to 24 Modem, rest was spare. Use 27-31 for modem control flags (fx.25 il2p etc)
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
int Freq;
|
|
|
|
Byte versionBytes[4] = VersionBytes;
|
|
|
|
|
|
|
|
AGW->reportFreqAndModem = 1; // Can report frequency and Modem
|
|
|
|
|
|
|
|
memcpy(&Freq, Data, 4);
|
|
|
|
|
|
|
|
if (Freq)
|
|
|
|
{
|
|
|
|
// Set Frequency
|
|
|
|
|
|
|
|
memcpy(&rx_freq[Frame->Port], Data, 2);
|
|
|
|
refreshModems = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Data[4])
|
|
|
|
{
|
|
|
|
// New Modem Name. Need to convert to index unless numeric
|
|
|
|
|
|
|
|
int n;
|
2023-12-17 13:53:32 +00:00
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
if (strlen(&Data[4]) < 3)
|
|
|
|
{
|
|
|
|
n = atoi(&Data[4]);
|
|
|
|
if (n < modes_count)
|
|
|
|
{
|
|
|
|
speed[Frame->Port] = n;
|
|
|
|
refreshModems = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (n = 0; n < modes_count; n++)
|
|
|
|
{
|
|
|
|
if (strcmp(modes_name[n], &Data[4]) == 0)
|
|
|
|
{
|
|
|
|
// Found it
|
|
|
|
|
|
|
|
speed[Frame->Port] = n;
|
|
|
|
refreshModems = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 13:53:32 +00:00
|
|
|
if (Data[27] == 2)
|
|
|
|
{
|
|
|
|
fx25_mode[Frame->Port] = Data[28];
|
|
|
|
il2p_mode[Frame->Port] = Data[29];
|
|
|
|
il2p_crc[Frame->Port] = Data[30];
|
|
|
|
}
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
// Return Freq and Modem
|
|
|
|
|
|
|
|
memcpy(&info[12], &rx_freq[Frame->Port], 2);
|
|
|
|
memcpy(&info[16], modes_name[speed[Frame->Port]], 20);
|
|
|
|
info[37] = speed[Frame->Port]; // Index
|
|
|
|
memcpy(&info[38], versionBytes, 4);
|
|
|
|
|
|
|
|
Len = 44;
|
2023-12-17 13:53:32 +00:00
|
|
|
|
|
|
|
if (Data[27])
|
|
|
|
{
|
|
|
|
// BPQ understands fx25 and il2p fields
|
|
|
|
|
|
|
|
AGW->reportFreqAndModem = 2; // Can report frequency Modem and flags
|
|
|
|
|
|
|
|
|
|
|
|
Len = 48;
|
|
|
|
|
|
|
|
info[44] = 1; // Show includes Modem Flags
|
|
|
|
info[45] = fx25_mode[Frame->Port];
|
|
|
|
info[46] = il2p_mode[Frame->Port];
|
|
|
|
info[47] = il2p_crc[Frame->Port];
|
|
|
|
}
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len));
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
void on_AGW_H_Frame(socket integer; port char);
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
void on_AGW_Ys_Frame(socket integer; port char);
|
|
|
|
var
|
|
|
|
snd_ch,i byte;
|
|
|
|
info string;
|
|
|
|
frames word;
|
|
|
|
{
|
|
|
|
frames = 0;
|
|
|
|
//for i = 0 to port_num-1 do frames = frames+AX25port[i].frame_buf.Count;
|
|
|
|
snd_ch = ord(Port)+1;
|
|
|
|
for i = 0 to port_num-1 do frames = frames+AX25port[snd_ch][i].in_data_buf.Count+AX25port[snd_ch][i].I_frame_buf.Count;
|
|
|
|
info = chr(lo(frames))+chr(hi(frames))+#0#0;
|
|
|
|
AGW_send_to_app(socket,AGW_Ys_Frame(port,info));
|
|
|
|
};
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
void on_AGW_Y_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo)
|
|
|
|
{
|
|
|
|
int frames;
|
|
|
|
TAX25Port * AX25Sess;
|
|
|
|
|
|
|
|
AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);
|
|
|
|
|
|
|
|
if (AX25Sess)
|
|
|
|
{
|
|
|
|
//frames = AX25port[snd_ch][user_port].in_data_buf.Count;
|
|
|
|
frames = AX25Sess->in_data_buf.Count + AX25Sess->I_frame_buf.Count;
|
|
|
|
;
|
|
|
|
AGW_send_to_app(socket, AGW_Y_Frame(snd_ch, CallFrom, CallTo, frames));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UI Transmit
|
|
|
|
|
|
|
|
void on_AGW_M_frame(int port, Byte PID, char * CallFrom, char *CallTo, Byte * Msg, int MsgLen)
|
|
|
|
{
|
|
|
|
Byte path[80];
|
|
|
|
char Calls[80];
|
|
|
|
string * Data = newString();
|
|
|
|
|
|
|
|
stringAdd(Data, Msg, MsgLen);
|
|
|
|
|
|
|
|
sprintf(Calls, "%s,%s", CallTo, CallFrom);
|
|
|
|
|
|
|
|
get_addr(Calls, path);
|
|
|
|
|
|
|
|
Add(&all_frame_buf[port],
|
|
|
|
make_frame(Data, path, PID, 0, 0, U_FRM, U_UI, FALSE, SET_F, SET_C));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_C_frame(AGWUser * AGW, struct AGWHeader * Frame)
|
|
|
|
{
|
|
|
|
int snd_ch = Frame->Port;
|
|
|
|
char * CallFrom = Frame->callfrom;
|
|
|
|
char * CallTo = Frame->callto;
|
|
|
|
|
|
|
|
char path[128];
|
|
|
|
Byte axpath[80];
|
|
|
|
|
|
|
|
TAX25Port * AX25Sess;
|
|
|
|
|
|
|
|
// Also used for 'v' - connect via digis
|
|
|
|
|
|
|
|
AX25Sess = get_free_port(snd_ch);
|
|
|
|
|
|
|
|
if (AX25Sess)
|
|
|
|
{
|
|
|
|
AX25Sess->snd_ch = snd_ch;
|
|
|
|
|
|
|
|
strcpy(AX25Sess->mycall, CallFrom);
|
|
|
|
strcpy(AX25Sess->corrcall, CallTo);
|
|
|
|
|
|
|
|
sprintf(path, "%s,%s", CallTo, CallFrom);
|
|
|
|
|
|
|
|
|
|
|
|
if (Frame->DataLength)
|
|
|
|
{
|
|
|
|
// Have digis
|
|
|
|
|
|
|
|
char * Digis = (char *)Frame + 36;
|
|
|
|
int nDigis = Digis[0];
|
|
|
|
|
|
|
|
Digis++;
|
|
|
|
|
|
|
|
while(nDigis--)
|
|
|
|
{
|
|
|
|
sprintf(path, "%s,%s", path, Digis);
|
|
|
|
Digis += 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AX25Sess->digi[0] = 0;
|
|
|
|
|
|
|
|
// rst_timer(snd_ch, free_port);
|
|
|
|
|
|
|
|
strcpy(AX25Sess->kind, "Outgoing");
|
|
|
|
AX25Sess->socket = AGW->socket;
|
|
|
|
|
|
|
|
AX25Sess->pathLen = get_addr(path, axpath);
|
|
|
|
|
|
|
|
if (AX25Sess->pathLen == 0)
|
|
|
|
return; // Invalid Path
|
|
|
|
|
|
|
|
strcpy((char *)AX25Sess->Path, (char *)axpath);
|
|
|
|
reverse_addr(axpath, AX25Sess->ReversePath, AX25Sess->pathLen);
|
|
|
|
|
|
|
|
if (RSID_SABM[snd_ch]) // Send RSID before SABM/UA
|
|
|
|
needRSID[snd_ch] = 1;
|
|
|
|
|
|
|
|
set_link(AX25Sess, AX25Sess->Path);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void on_AGW_D_frame(int snd_ch, char * CallFrom, char * CallTo, Byte * Msg, int Len)
|
|
|
|
{
|
|
|
|
TAX25Port * AX25Sess;
|
|
|
|
|
|
|
|
AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);
|
|
|
|
|
|
|
|
if (AX25Sess)
|
|
|
|
{
|
|
|
|
string * data = newString();
|
|
|
|
|
|
|
|
stringAdd(data, Msg, Len);
|
|
|
|
|
|
|
|
Add(&AX25Sess->in_data_buf, data);
|
|
|
|
|
|
|
|
send_data_buf(AX25Sess, AX25Sess->vs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_AGW_Ds_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo)
|
|
|
|
{
|
|
|
|
TAX25Port * AX25Sess;
|
|
|
|
|
|
|
|
AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo);
|
|
|
|
|
|
|
|
if (AX25Sess)
|
|
|
|
{
|
|
|
|
rst_timer(AX25Sess);
|
|
|
|
|
|
|
|
set_unlink(AX25Sess, AX25Sess->Path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string * Msg = newString();
|
|
|
|
|
|
|
|
Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", CallTo);
|
|
|
|
Msg->Length++; // Include the terminating NULL
|
|
|
|
|
|
|
|
//del_outgoing_mycalls(CallTo);
|
|
|
|
|
|
|
|
AGW_send_to_app(socket, AGW_Ds_Frame(snd_ch, CallTo, CallFrom, Msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
void on_AGW_Vs_Frame(socket integer; port char; CallFrom,CallTo,Data string);
|
|
|
|
var
|
|
|
|
snd_ch,num_digi,free_port byte;
|
|
|
|
need_free_port boolean;
|
|
|
|
digi,call,call1 string;
|
|
|
|
{
|
|
|
|
snd_ch = ord(Port)+1;
|
|
|
|
num_digi = 0;
|
|
|
|
need_free_port = get_free_port(snd_ch,free_port);
|
|
|
|
if (need_free_port )
|
|
|
|
{
|
|
|
|
digi = '';
|
|
|
|
get_call(CallFrom,AX25Port[snd_ch][free_port].mycall);
|
|
|
|
get_call(CallTo,AX25Port[snd_ch][free_port].corrcall);
|
|
|
|
if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); };
|
|
|
|
if ((num_digi in [1..7]) and (length(data)>=num_digi*10) )
|
|
|
|
{
|
|
|
|
repeat
|
|
|
|
call = clr_zero(copy(data,1,10)); delete(data,1,10);
|
|
|
|
if (call<>'' )
|
|
|
|
{
|
|
|
|
get_call(call,call1);
|
|
|
|
if (digi='' ) digi = call1
|
|
|
|
else digi = digi+','+call1;
|
|
|
|
};
|
|
|
|
until data='';
|
|
|
|
AX25Port[snd_ch][free_port].digi = reverse_digi(digi);
|
|
|
|
rst_timer(snd_ch,free_port);
|
|
|
|
AX25Port[snd_ch][free_port].kind = 'Outgoing';
|
|
|
|
AX25Port[snd_ch][free_port].socket = socket;
|
|
|
|
set_link(snd_ch,free_port,CallTo+','+CallFrom);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
void on_AGW_V_Frame(socket integer; port char; PID,CallFrom,CallTo,Data string);
|
|
|
|
var
|
|
|
|
call,call1,digi,path string;
|
|
|
|
snd_ch byte;
|
|
|
|
num_digi byte;
|
|
|
|
i byte;
|
|
|
|
{
|
|
|
|
digi = '';
|
|
|
|
snd_ch = ord(port)+1;
|
|
|
|
num_digi = 0;
|
|
|
|
if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); };
|
|
|
|
if ((num_digi in [1..7]) and (length(data)>=num_digi*10) )
|
|
|
|
{
|
|
|
|
for i = 1 to num_digi do
|
|
|
|
{
|
|
|
|
call = clr_zero(copy(data,1,10)); delete(data,1,10);
|
|
|
|
if (call<>'' )
|
|
|
|
{
|
|
|
|
get_call(call,call1);
|
|
|
|
if (digi='' ) digi = call1
|
|
|
|
else digi = digi+','+call1;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
path = CallTo+','+CallFrom+','+digi;
|
|
|
|
all_frame_buf[snd_ch].Add(make_frame(Data,path,ord(PID[1]),0,0,U_FRM,U_UI,FALSE,SET_F,SET_C));
|
|
|
|
};
|
|
|
|
|
|
|
|
void on_AGW_Cs_Frame(socket integer; port char; PID,CallFrom,CallTo string);
|
|
|
|
{
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
void on_AGW_K_frame(struct AGWHeader * Frame)
|
|
|
|
{
|
|
|
|
// KISS frame
|
|
|
|
|
|
|
|
unsigned short CRC;
|
|
|
|
UCHAR CRCString[2];
|
|
|
|
string * TXMSG;
|
|
|
|
|
|
|
|
UCHAR * Data = (UCHAR * )Frame;
|
|
|
|
int Len = Frame->DataLength;
|
|
|
|
|
|
|
|
Data = &Data[AGWHDDRRLEN];
|
|
|
|
|
|
|
|
TXMSG = newString();
|
|
|
|
|
|
|
|
stringAdd(TXMSG, Data, Len); // include Control
|
|
|
|
|
|
|
|
CRC = get_fcs(&Data[1], Len - 1);
|
|
|
|
|
|
|
|
CRCString[0] = CRC & 0xff;
|
|
|
|
CRCString[1] = CRC >> 8;
|
|
|
|
|
|
|
|
stringAdd(TXMSG, CRCString, 2);
|
|
|
|
|
|
|
|
Add(&all_frame_buf[Frame->Port], TXMSG);
|
|
|
|
|
|
|
|
// for now assume only used for sending UI
|
|
|
|
|
|
|
|
if (RSID_UI[Frame->Port]) // Send RSID before UI
|
|
|
|
needRSID[Frame->Port] = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_AGW_Ks_frame(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
AGW->Monitor_raw ^= 1; // Flip State
|
|
|
|
}
|
|
|
|
|
|
|
|
// Analiz incoming frames
|
|
|
|
|
|
|
|
void AGW_explode_frame(void * socket, UCHAR * data, int length)
|
|
|
|
{
|
|
|
|
int AGWHeaderLen = sizeof(struct AGWHeader);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
AGWUser * AGW = NULL;
|
|
|
|
|
|
|
|
if (AGWConCount == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < AGWConCount; i++)
|
|
|
|
{
|
|
|
|
if (AGWUsers[i]->socket == socket)
|
|
|
|
{
|
|
|
|
AGW = AGWUsers[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AGW == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
stringAdd(AGW->data_in, data, length);
|
|
|
|
|
|
|
|
while (AGW->data_in->Length >= AGWHeaderLen) // Make sure have at least header
|
|
|
|
{
|
|
|
|
struct AGWHeader * Hddr = (struct AGWHeader *)AGW->data_in->Data;
|
|
|
|
|
|
|
|
int AgwLen = Hddr->DataLength + AGWHeaderLen;
|
|
|
|
|
|
|
|
if (AgwLen < AGWHeaderLen) // Corrupt
|
|
|
|
{
|
|
|
|
AGW->data_in->Length = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AGW->data_in->Length >= AgwLen)
|
|
|
|
{
|
|
|
|
// Have frame as well
|
|
|
|
|
|
|
|
if (AGW->data_in->Data[0] == 0xC0) // Getting KISS Data on AGW Port
|
|
|
|
{
|
|
|
|
AGW->data_in->Length = 0; // Delete data
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AGW_frame_analiz(AGW);
|
|
|
|
mydelete(AGW->data_in, 0, AgwLen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return; // Wait for the data
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
idx = AGW_get_socket(socket);
|
|
|
|
if (idx>=0 )
|
|
|
|
{
|
|
|
|
AGWUser.AGW_frame_buf.Strings[idx] = AGWUser.AGW_frame_buf.Strings[idx]+data;
|
|
|
|
str_buf = AGWUser.AGW_frame_buf.Strings[idx];
|
|
|
|
repeat
|
|
|
|
done = FALSE;
|
|
|
|
BufLen = length(str_buf);
|
|
|
|
if (BufLen>=HEADER_SIZE )
|
|
|
|
{
|
|
|
|
DataLen = ord(str_buf[LSB])+ord(str_buf[MSB])*256;
|
|
|
|
FrameLen = HEADER_SIZE+DataLen;
|
|
|
|
if (length(str_buf)>=FrameLen )
|
|
|
|
{
|
|
|
|
frame = copy(str_buf,1,FrameLen);
|
|
|
|
delete(str_buf,1,FrameLen);
|
|
|
|
done = TRUE;
|
|
|
|
AGW_frame_analiz(socket,frame);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
until not done;
|
|
|
|
// Check if (socket is still present and has same index
|
|
|
|
if ((AGWUser.socket.Count>idx) and (AGWUser.socket.Strings[idx]=inttostr(socket)) )
|
|
|
|
AGWUser.AGW_frame_buf.Strings[idx] = str_buf;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
void clr_zero(data string) string;
|
|
|
|
var
|
|
|
|
p,i word;
|
|
|
|
s string;
|
|
|
|
{
|
|
|
|
s = '';
|
|
|
|
p = pos(#0,data);
|
|
|
|
if (p>1 ) data = copy(data,1,p-1);
|
|
|
|
if (length(data)>0 ) for i = 1 to length(data) do if (data[i]>#31 ) s = s+data[i];
|
|
|
|
result = s;
|
|
|
|
};
|
|
|
|
|
|
|
|
void AGW_parse_frame(frame string; var DataKind,PID,AGWPort char; var Pass,CallFrom,CallTo,DataLen,Data string);
|
|
|
|
{
|
|
|
|
DataKind = frame[5];
|
|
|
|
PID = frame[7];
|
|
|
|
AGWPort = frame[1];
|
|
|
|
Pass = '';
|
|
|
|
CallFrom = clr_zero(copy(frame,9,10));
|
|
|
|
CallTo = clr_zero(copy(frame,19,10));
|
|
|
|
get_call(CallFrom,CallFrom);
|
|
|
|
get_call(CallTo,CallTo);
|
|
|
|
DataLen = inttostr(ord(frame[LSB])+ord(frame[MSB])*256);
|
|
|
|
if (length(frame)>HEADER_SIZE ) Data = copy(frame,37,strtoint(DataLen)) else Data = '';
|
|
|
|
};
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
void AGW_send_to_app(void * socket, string * data)
|
|
|
|
{
|
|
|
|
char * Msg = malloc(data->Length);
|
|
|
|
memcpy(Msg, data->Data, data->Length);
|
|
|
|
// can use KISS proc as it just sends to the supplied socket but need copy of message
|
|
|
|
KISSSendtoServer(socket, (Byte *)Msg, data->Length);
|
|
|
|
freeString(data);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_AX25_data_in(void * socket, int snd_ch, int PID, Byte * path, string * data)
|
|
|
|
{
|
|
|
|
int len = 250, sendlen;
|
|
|
|
|
|
|
|
char CallFrom[10];
|
|
|
|
char CallTo[10];
|
|
|
|
|
|
|
|
string * pkt;
|
|
|
|
|
|
|
|
CallTo[ConvFromAX25(&path[7], CallTo)] = 0;
|
|
|
|
CallFrom[ConvFromAX25(path, CallFrom)] = 0;
|
|
|
|
|
|
|
|
while (data->Length)
|
|
|
|
{
|
|
|
|
if (data->Length > len)
|
|
|
|
sendlen = len;
|
|
|
|
else
|
|
|
|
sendlen = data->Length;
|
|
|
|
|
|
|
|
pkt = copy(data, 0, sendlen);
|
|
|
|
mydelete(data, 0, sendlen);
|
|
|
|
|
|
|
|
AGW_send_to_app(socket, AGW_D_Frame(snd_ch, PID, CallFrom, CallTo, pkt));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void AGW_AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode)
|
|
|
|
{
|
|
|
|
string * Msg = newString();
|
|
|
|
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case MODE_OTHER:
|
|
|
|
|
|
|
|
Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED To Station %s\r", AX25Sess->corrcall);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_OUR:
|
|
|
|
|
|
|
|
Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED With Station %s\r", AX25Sess->corrcall);
|
|
|
|
break;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Msg->Length++; // Include the terminating NULL
|
|
|
|
|
|
|
|
AGW_send_to_app(AX25Sess->socket, AGW_C_Frame(snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_AX25_disc(TAX25Port * AX25Sess, Byte mode)
|
|
|
|
{
|
|
|
|
string * Msg = newString();
|
|
|
|
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
|
|
|
|
case MODE_OTHER:
|
|
|
|
case MODE_OUR:
|
|
|
|
|
|
|
|
Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", AX25Sess->corrcall);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_RETRY:
|
|
|
|
|
|
|
|
Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED RETRYOUT With Station %s\r", AX25Sess->corrcall);
|
|
|
|
break;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Msg->Length++; // Include the terminating NULL
|
|
|
|
|
|
|
|
//del_outgoing_mycalls(CallTo);
|
|
|
|
|
|
|
|
AGW_send_to_app(AX25Sess->socket, AGW_Ds_Frame(AX25Sess->snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_frame_monitor(Byte snd_ch, Byte * path, string * data, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, Byte rpt, Byte pf, Byte cr, Byte RX)
|
|
|
|
{
|
|
|
|
char mon_frm[512];
|
|
|
|
char AGW_path[256];
|
|
|
|
string * AGW_data = NULL;
|
|
|
|
|
|
|
|
const char * frm;
|
|
|
|
Byte * datap = data->Data;
|
|
|
|
Byte _data[512];
|
|
|
|
Byte * p_data = _data;
|
|
|
|
int _datalen;
|
|
|
|
|
|
|
|
char agw_port;
|
|
|
|
char CallFrom[10], CallTo[10], Digi[80];
|
|
|
|
|
|
|
|
integer i;
|
|
|
|
const char * ctrl;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
AGWUser * AGW;
|
|
|
|
|
|
|
|
UNUSED(rpt);
|
|
|
|
|
|
|
|
len = data->Length;
|
|
|
|
|
|
|
|
// if (pid == 0xCF)
|
|
|
|
// data = parse_NETROM(data, f_id);
|
|
|
|
// IP parsing
|
|
|
|
// else if (pid == 0xCC)
|
|
|
|
// data = parse_IP(data);
|
|
|
|
// ARP parsing
|
|
|
|
// else if (pid == 0xCD)
|
|
|
|
// data = parse_ARP(data);
|
|
|
|
//
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9)
|
|
|
|
*(p_data++) = datap[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_datalen = p_data - _data;
|
|
|
|
|
|
|
|
if (_datalen)
|
|
|
|
{
|
|
|
|
Byte * ptr = _data;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
// remove successive cr or cr on end while (i < _datalen)
|
|
|
|
|
|
|
|
while (i < _datalen)
|
|
|
|
{
|
|
|
|
if ((_data[i] == 13) && (_data[i + 1] == 13))
|
|
|
|
i++;
|
|
|
|
else
|
|
|
|
*(ptr++) = _data[i++];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*(ptr - 1) == 13)
|
|
|
|
ptr--;
|
|
|
|
|
|
|
|
*ptr = 0;
|
|
|
|
|
|
|
|
_datalen = ptr - _data;
|
|
|
|
}
|
|
|
|
|
|
|
|
agw_port = snd_ch;
|
|
|
|
|
|
|
|
get_monitor_path(path, CallTo, CallFrom, Digi);
|
|
|
|
|
|
|
|
ctrl = "";
|
|
|
|
frm = "";
|
|
|
|
|
|
|
|
switch (f_id)
|
|
|
|
{
|
|
|
|
case I_I:
|
|
|
|
|
|
|
|
frm = "I";
|
|
|
|
if (cr == SET_C && pf == SET_P)
|
|
|
|
ctrl = " P";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_RR:
|
|
|
|
|
|
|
|
frm = "RR";
|
|
|
|
if (pf == SET_P)
|
|
|
|
ctrl = " P/F";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_RNR:
|
|
|
|
|
|
|
|
frm = "RNR";
|
|
|
|
if (pf == SET_P)
|
|
|
|
ctrl = " P/F";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_REJ:
|
|
|
|
|
|
|
|
frm = "REJ";
|
|
|
|
if (pf == SET_P)
|
|
|
|
ctrl = " P/F";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case S_SREJ:
|
|
|
|
|
|
|
|
frm = "SREJ";
|
|
|
|
if (pf == SET_P)
|
|
|
|
ctrl = " P/F";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_SABM:
|
|
|
|
|
|
|
|
frm = "SABM";
|
|
|
|
if (cr == SET_C && pf == SET_P)
|
|
|
|
ctrl = " P";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_DISC:
|
|
|
|
|
|
|
|
frm = "DISC";
|
|
|
|
if (cr == SET_C && pf == SET_P)
|
|
|
|
ctrl = " P";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_DM:
|
|
|
|
|
|
|
|
frm = "DM";
|
|
|
|
if ((cr == SET_R) && (pf == SET_P))
|
|
|
|
ctrl = " F ";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_UA:
|
|
|
|
|
|
|
|
frm = "UA";
|
|
|
|
if ((cr == SET_R) && (pf == SET_P))
|
|
|
|
ctrl = " F ";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_FRMR:
|
|
|
|
|
|
|
|
frm = "FRMR";
|
|
|
|
if ((cr == SET_R) && (pf == SET_P))
|
|
|
|
ctrl = " F ";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_UI:
|
|
|
|
|
|
|
|
frm = "UI";
|
|
|
|
if ((pf == SET_P))
|
|
|
|
ctrl = " P/F";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Digi[0])
|
|
|
|
sprintf(AGW_path, " %d:Fm %s To %s Via %s <%s", snd_ch + 1, CallFrom, CallTo, Digi, frm);
|
|
|
|
else
|
|
|
|
sprintf(AGW_path, " %d:Fm %s To %s <%s", snd_ch + 1, CallFrom, CallTo, frm);
|
|
|
|
|
|
|
|
|
|
|
|
switch (f_type)
|
|
|
|
{
|
|
|
|
case I_FRM:
|
|
|
|
|
|
|
|
//mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13;
|
|
|
|
sprintf(mon_frm, "%s%s R%d S%d pid=%X Len=%d >[%s]\r%s\r", AGW_path, ctrl, nr, ns, pid, len, ShortDateTime(), _data);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_FRM:
|
|
|
|
|
|
|
|
if (f_id == U_UI)
|
|
|
|
{
|
|
|
|
sprintf(mon_frm, "%s pid=%X Len=%d >[%s]\r%s\r", AGW_path, pid, len, ShortDateTime(), _data); // "= AGW_path + ctrl + '>' + time_now + #13;
|
|
|
|
}
|
|
|
|
else if (f_id == U_FRMR)
|
|
|
|
{
|
|
|
|
sprintf(mon_frm, "%s%s>%02x %02x %02x[%s]\r", AGW_path, ctrl, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(mon_frm, "%s%s>[%s]\r", AGW_path, ctrl, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_FRM:
|
|
|
|
|
|
|
|
// mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13;
|
|
|
|
sprintf(mon_frm, "%s%s R%d>[%s]\r", AGW_path, ctrl, nr, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// stringAdd(mon_frm, "", 1); // Add 0 at the end of each frame
|
|
|
|
|
|
|
|
// I think we send to all AGW sockets
|
|
|
|
|
|
|
|
for (i = 0; i < AGWConCount; i++)
|
|
|
|
{
|
|
|
|
AGW = AGWUsers[i];
|
|
|
|
|
|
|
|
if (AGW->Monitor)
|
|
|
|
{
|
|
|
|
if (RX)
|
|
|
|
{
|
|
|
|
switch (f_id)
|
|
|
|
{
|
|
|
|
|
|
|
|
case I_I:
|
|
|
|
AGW_data = AGW_I_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_RR:
|
|
|
|
case S_RNR:
|
|
|
|
case S_REJ:
|
|
|
|
case S_SREJ:
|
|
|
|
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_SABM:
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_DISC:
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_DM:
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_UA:
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_FRMR:
|
|
|
|
AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case U_UI:
|
|
|
|
AGW_data = AGW_U_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
}
|
|
|
|
if (AGW_data)
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AGW_data = AGW_T_Frame(agw_port, CallFrom, CallTo, mon_frm);
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AGW_Report_Modem_Change(int port)
|
|
|
|
{
|
|
|
|
// Send modem change report to all sockets that support it
|
|
|
|
|
|
|
|
int i;
|
|
|
|
AGWUser * AGW;
|
|
|
|
string * pkt;
|
|
|
|
|
2023-12-17 13:53:32 +00:00
|
|
|
if (soundChannel[port] == 0) // Not in use
|
|
|
|
return;
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
// I think we send to all AGW sockets
|
|
|
|
|
|
|
|
for (i = 0; i < AGWConCount; i++)
|
|
|
|
{
|
|
|
|
AGW = AGWUsers[i];
|
|
|
|
|
|
|
|
if (AGW->reportFreqAndModem)
|
|
|
|
{
|
2023-12-17 13:53:32 +00:00
|
|
|
// QTSM 'g' Message with a data field is used by QtSM to set/read Modem Params
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-12-17 13:53:32 +00:00
|
|
|
Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
|
|
|
|
int Len = 44;
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
// Return Freq and Modem
|
|
|
|
|
|
|
|
memcpy(&info[12], &rx_freq[port], 2);
|
|
|
|
memcpy(&info[16], modes_name[speed[port]], 20);
|
|
|
|
info[37] = speed[port]; // Index
|
2023-12-17 13:53:32 +00:00
|
|
|
|
|
|
|
if (AGW->reportFreqAndModem == 2)
|
|
|
|
{
|
|
|
|
Len = 48;
|
|
|
|
|
|
|
|
info[44] = 1; // Show includes Modem Flags
|
|
|
|
info[45] = fx25_mode[port];
|
|
|
|
info[46] = il2p_mode[port];
|
|
|
|
info[47] = il2p_crc[port];
|
|
|
|
}
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_Gs_Frame(port, info, Len));
|
2023-09-04 19:06:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_Raw_monitor(int snd_ch, string * data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
AGWUser * AGW;
|
|
|
|
string * pkt;
|
|
|
|
|
|
|
|
// I think we send to all AGW sockets
|
|
|
|
|
|
|
|
for (i = 0; i < AGWConCount; i++)
|
|
|
|
{
|
|
|
|
AGW = AGWUsers[i];
|
|
|
|
|
|
|
|
if (AGW->Monitor_raw)
|
|
|
|
{
|
|
|
|
pkt = newString();
|
|
|
|
|
|
|
|
pkt->Data[0] = snd_ch << 4; // KISS Address
|
|
|
|
pkt->Length++;
|
|
|
|
|
|
|
|
stringAdd(pkt, data->Data, data->Length - 2); // Exclude CRC
|
|
|
|
|
|
|
|
AGW_send_to_app(AGW->socket, AGW_K_Frame(snd_ch, 0, "", "", pkt));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame)
|
|
|
|
{
|
|
|
|
// path,data string;
|
|
|
|
Byte pid, nr, ns, f_type, f_id;
|
|
|
|
Byte rpt, cr, pf;
|
|
|
|
Byte path[80];
|
|
|
|
string * data = newString();
|
|
|
|
|
|
|
|
decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr);
|
|
|
|
|
|
|
|
AGW_frame_monitor(snd_ch, path, data, pid, nr, ns, f_type, f_id, rpt, pf, cr, RX);
|
|
|
|
|
|
|
|
// if (RX)
|
|
|
|
// AGW_Raw_monitor(snd_ch, frame);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void AGW_frame_analiz(AGWUser * AGW)
|
|
|
|
{
|
|
|
|
struct AGWHeader * Frame = (struct AGWHeader *)AGW->data_in->Data;
|
|
|
|
Byte * Data = &AGW->data_in->Data[36];
|
|
|
|
|
|
|
|
if (Frame->Port < 0 || Frame->Port > 3)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (soundChannel[Frame->Port] == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (Frame->Port > 3)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (Frame->DataKind)
|
|
|
|
{
|
|
|
|
case 'P':
|
|
|
|
|
|
|
|
on_AGW_P_frame(AGW);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'X':
|
|
|
|
|
|
|
|
on_AGW_X_frame(AGW, Frame->callfrom);
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
case 'x':
|
|
|
|
|
|
|
|
on_AGW_Xs_frame(Frame->callfrom);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'G':
|
|
|
|
|
|
|
|
on_AGW_G_frame(AGW);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'm':
|
|
|
|
|
|
|
|
on_AGW_Ms_frame(AGW);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'R':
|
|
|
|
|
|
|
|
on_AGW_R_frame(AGW);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'g':
|
|
|
|
|
|
|
|
on_AGW_Gs_frame(AGW, Frame, Data);
|
|
|
|
return;
|
|
|
|
// 'H': on_AGW_H_frame(AGW,Frame->Port);
|
|
|
|
// 'y': on_AGW_Ys_frame(AGW,Frame->Port);
|
|
|
|
|
|
|
|
case 'Y':
|
|
|
|
on_AGW_Y_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
|
|
|
|
|
|
|
on_AGW_M_frame(Frame->Port,Frame->PID, Frame->callfrom, Frame->callto, Data, Frame->DataLength);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
case 'v': // Call with digis
|
|
|
|
|
|
|
|
on_AGW_C_frame(AGW, Frame);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'D':
|
|
|
|
|
|
|
|
on_AGW_D_frame(Frame->Port, Frame->callfrom, Frame->callto, Data, Frame->DataLength);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
on_AGW_Ds_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto);
|
|
|
|
return;
|
|
|
|
|
|
|
|
// 'V': on_AGW_V_frame(AGW,Frame->Port,PID,CallFrom,CallTo,Data);
|
|
|
|
// 'c': on_AGW_Cs_frame(sAGWocket,Frame->Port,PID,CallFrom,CallTo);
|
|
|
|
|
|
|
|
|
|
|
|
case 'K':
|
|
|
|
|
|
|
|
on_AGW_K_frame(Frame);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'k':
|
|
|
|
on_AGW_Ks_frame(AGW);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Debugprintf("AGW %c", Frame->DataKind);
|
|
|
|
}
|
|
|
|
}
|