480 lines
8.6 KiB
C
480 lines
8.6 KiB
C
/*
|
|
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"
|
|
|
|
int add_raw_frames(int snd_ch, string * frame, TStringList * buf);
|
|
|
|
// I think I need a struct for each connection, but a simple array of entries should be fine
|
|
// My normal ** and count system
|
|
// Each needs an input buffer of max size kiss frame and length (or maybe string is a good idea)
|
|
|
|
TKISSMode ** KissConnections = NULL;
|
|
int KISSConCount = 0;
|
|
|
|
#define FEND 0xc0
|
|
#define FESC 0xDB
|
|
#define TFEND 0xDC
|
|
#define TFESC 0xDD
|
|
#define KISS_ACKMODE 0x0C
|
|
#define KISS_DATA 0
|
|
#define QTSMKISSCMD 7
|
|
|
|
struct TKISSMode_t KISS;
|
|
|
|
int KISS_encode(UCHAR * KISSBuffer, int port, string * frame, int TXMON);
|
|
|
|
void KISS_init()
|
|
{
|
|
int i;
|
|
|
|
KISS.data_in = newString();
|
|
|
|
// initTStringList(KISS.socket);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
initTStringList(&KISS.buffer[i]);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
procedure KISS_free;
|
|
var
|
|
i: byte;
|
|
begin
|
|
KISS.data_in.Free;
|
|
KISS.socket.Free;
|
|
for i:=1 to 4 do
|
|
begin
|
|
KISS.buffer[i].Free;
|
|
KISS.request[i].Free;
|
|
KISS.acked[i].Free;
|
|
KISS.irequest[i].Free;
|
|
KISS.iacked[i].Free;
|
|
end;
|
|
end;
|
|
*/
|
|
|
|
void KISS_add_stream(void * Socket)
|
|
{
|
|
// Add a new connection. Called when QT accepts an incoming call}
|
|
|
|
TKISSMode * KISS;
|
|
|
|
KissConnections = realloc(KissConnections, (KISSConCount + 1) * sizeof(void *));
|
|
|
|
KISS = KissConnections[KISSConCount++] = malloc(sizeof(*KISS));
|
|
memset(KISS, 0, sizeof(*KISS));
|
|
|
|
KISS->Socket = Socket;
|
|
KISS->data_in = newString();
|
|
|
|
}
|
|
|
|
void KISS_del_socket(void * socket)
|
|
{
|
|
int i;
|
|
|
|
TKISSMode * KISS = NULL;
|
|
|
|
if (KISSConCount == 0)
|
|
return;
|
|
|
|
for (i = 0; i < KISSConCount; i++)
|
|
{
|
|
if (KissConnections[i]->Socket == socket)
|
|
{
|
|
KISS = KissConnections[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KISS == NULL)
|
|
return;
|
|
|
|
// Need to remove entry and move others down
|
|
|
|
KISSConCount--;
|
|
|
|
while (i < KISSConCount)
|
|
{
|
|
KissConnections[i] = KissConnections[i + 1];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
|
|
void KISS_on_data_out(int port, string * frame, int TX)
|
|
{
|
|
int Len;
|
|
UCHAR * KISSFrame = (UCHAR *)malloc(512); // cant pass local data via signal/slot
|
|
|
|
Len = KISS_encode(KISSFrame, port, frame, TX);
|
|
|
|
KISSSendtoServer(NULL, KISSFrame, Len); // Send to all open sockets
|
|
}
|
|
|
|
void ProcessKISSFrame(void * socket, UCHAR * Msg, int Len)
|
|
{
|
|
int n = Len;
|
|
UCHAR c;
|
|
int ESCFLAG = 0;
|
|
UCHAR * ptr1, *ptr2;
|
|
int Chan;
|
|
int Opcode;
|
|
string * TXMSG;
|
|
unsigned short CRC;
|
|
UCHAR CRCString[2];
|
|
|
|
ptr1 = ptr2 = Msg;
|
|
|
|
while (n--)
|
|
{
|
|
c = *(ptr1++);
|
|
|
|
if (ESCFLAG)
|
|
{
|
|
//
|
|
// FESC received - next should be TFESC or TFEND
|
|
|
|
ESCFLAG = 0;
|
|
|
|
if (c == TFESC)
|
|
c = FESC;
|
|
|
|
if (c == TFEND)
|
|
c = FEND;
|
|
|
|
}
|
|
else
|
|
{
|
|
switch (c)
|
|
{
|
|
case FEND:
|
|
|
|
//
|
|
// Either start of message or message complete
|
|
//
|
|
|
|
// npKISSINFO->MSGREADY = TRUE;
|
|
return;
|
|
|
|
case FESC:
|
|
|
|
ESCFLAG = 1;
|
|
continue;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ok, a normal char
|
|
//
|
|
|
|
*(ptr2++) = c;
|
|
|
|
}
|
|
Len = ptr2 - Msg;
|
|
|
|
Chan = (Msg[0] >> 4);
|
|
Opcode = Msg[0] & 0x0f;
|
|
|
|
if (Chan > 3)
|
|
return;
|
|
|
|
switch (Opcode)
|
|
{
|
|
case KISS_ACKMODE:
|
|
|
|
// How best to do ACKMODE?? I think pass whole frame including CMD and ack bytes to all_frame_buf
|
|
|
|
// But ack should only be sent to client that sent the message - needs more thought!
|
|
|
|
TXMSG = newString();
|
|
stringAdd(TXMSG, &Msg[0], Len); // include Control
|
|
|
|
CRC = get_fcs(&Msg[3], Len - 3); // exclude control and ack bytes
|
|
|
|
CRCString[0] = CRC & 0xff;
|
|
CRCString[1] = CRC >> 8;
|
|
|
|
stringAdd(TXMSG, CRCString, 2);
|
|
|
|
// Ackmode needs to know where to send ack back to, so save socket on end of data
|
|
|
|
stringAdd(TXMSG, (unsigned char * )&socket, sizeof(socket));
|
|
|
|
// if KISS Optimise see if frame is really needed
|
|
|
|
if (!KISS_opt[Chan])
|
|
Add(&KISS.buffer[Chan], TXMSG);
|
|
else
|
|
{
|
|
if (add_raw_frames(Chan, TXMSG, &KISS.buffer[Chan]))
|
|
Add(&KISS.buffer[Chan], TXMSG);
|
|
}
|
|
|
|
return;
|
|
|
|
case KISS_DATA:
|
|
|
|
TXMSG = newString();
|
|
stringAdd(TXMSG, &Msg[0], Len); // include Control
|
|
|
|
CRC = get_fcs(&Msg[1], Len - 1);
|
|
|
|
CRCString[0] = CRC & 0xff;
|
|
CRCString[1] = CRC >> 8;
|
|
|
|
stringAdd(TXMSG, CRCString, 2);
|
|
|
|
// if KISS Optimise see if frame is really needed
|
|
|
|
if (!KISS_opt[Chan])
|
|
Add(&KISS.buffer[Chan], TXMSG);
|
|
else
|
|
{
|
|
if (add_raw_frames(Chan, TXMSG, &KISS.buffer[Chan]))
|
|
Add(&KISS.buffer[Chan], TXMSG);
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
// Still need to process kiss control frames
|
|
}
|
|
|
|
|
|
|
|
|
|
void KISSDataReceived(void * socket, UCHAR * data, int length)
|
|
{
|
|
int i;
|
|
UCHAR * ptr1, * ptr2;
|
|
int Length;
|
|
|
|
TKISSMode * KISS = NULL;
|
|
|
|
if (KISSConCount == 0)
|
|
return;
|
|
|
|
for (i = 0; i < KISSConCount; i++)
|
|
{
|
|
if (KissConnections[i]->Socket == socket)
|
|
{
|
|
KISS = KissConnections[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KISS == NULL)
|
|
return;
|
|
|
|
stringAdd(KISS->data_in, data, length);
|
|
|
|
if (KISS->data_in->Length > 10000) // Probably AGW Data on KISS Port
|
|
{
|
|
KISS->data_in->Length = 0;
|
|
return;
|
|
}
|
|
|
|
ptr1 = KISS->data_in->Data;
|
|
Length = KISS->data_in->Length;
|
|
|
|
|
|
while ((ptr2 = memchr(ptr1, FEND, Length)))
|
|
{
|
|
int Len = (ptr2 - ptr1);
|
|
|
|
if (Len == 0)
|
|
{
|
|
// Start of frame
|
|
|
|
mydelete(KISS->data_in, 0, 1);
|
|
|
|
ptr1 = KISS->data_in->Data;
|
|
Length = KISS->data_in->Length;
|
|
|
|
continue;
|
|
}
|
|
|
|
// Process Frame
|
|
|
|
if (Len < 350) // Drop obviously corrupt frames
|
|
ProcessKISSFrame(socket, ptr1, Len);
|
|
|
|
mydelete(KISS->data_in, 0, Len + 1);
|
|
|
|
ptr1 = KISS->data_in->Data;
|
|
Length = KISS->data_in->Length;
|
|
|
|
}
|
|
|
|
/* if (length(KISS.data_in.Strings[idx]) > 65535)
|
|
if Form1.ServerSocket2.Socket.ActiveConnections > 0)
|
|
|
|
for i:=0 to Form1.ServerSocket2.Socket.ActiveConnections-1 do
|
|
if Form1.ServerSocket2.Socket.Connections[i].SocketHandle=socket then
|
|
try Form1.ServerSocket2.Socket.Connections[i].Close; except end;
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int KISS_encode(UCHAR * KISSBuffer, int port, string * frame, int TXMON)
|
|
{
|
|
|
|
// Encode frame
|
|
|
|
UCHAR * ptr1 = frame->Data;
|
|
UCHAR TXCCC = 0;
|
|
int Len = frame->Length - 2; // frame includes CRC
|
|
UCHAR * ptr2 = &KISSBuffer[2];
|
|
UCHAR c;
|
|
|
|
if (TXMON)
|
|
{
|
|
// TX Frame has control byte on front
|
|
|
|
ptr1++;
|
|
Len--;
|
|
}
|
|
|
|
KISSBuffer[0] = FEND;
|
|
KISSBuffer[1] = port << 4;
|
|
|
|
TXCCC ^= KISSBuffer[1];
|
|
|
|
while (Len--)
|
|
{
|
|
c = *(ptr1++);
|
|
TXCCC ^= c;
|
|
|
|
switch (c)
|
|
{
|
|
case FEND:
|
|
(*ptr2++) = FESC;
|
|
(*ptr2++) = TFEND;
|
|
break;
|
|
|
|
case FESC:
|
|
|
|
(*ptr2++) = FESC;
|
|
(*ptr2++) = TFESC;
|
|
break;
|
|
|
|
// Drop through
|
|
|
|
default:
|
|
|
|
(*ptr2++) = c;
|
|
}
|
|
}
|
|
|
|
// If using checksum, send it
|
|
/*
|
|
|
|
if (KISSFLAGS & CHECKSUM)
|
|
{
|
|
c = (UCHAR)KISS->TXCCC;
|
|
|
|
// On TNC-X based boards, it is difficult to cope with an encoded CRC, so if
|
|
// CRC is FEND, send it as 0xc1. This means we have to accept 00 or 01 as valid.
|
|
// which is a slight loss in robustness
|
|
|
|
if (c == FEND && (PORT->KISSFLAGS & TNCX))
|
|
{
|
|
(*ptr2++) = FEND + 1;
|
|
}
|
|
else
|
|
{
|
|
switch (c)
|
|
{
|
|
case FEND:
|
|
(*ptr2++) = FESC;
|
|
(*ptr2++) = TFEND;
|
|
break;
|
|
|
|
case FESC:
|
|
(*ptr2++) = FESC;
|
|
(*ptr2++) = TFESC;
|
|
break;
|
|
|
|
default:
|
|
(*ptr2++) = c;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
(*ptr2++) = FEND;
|
|
|
|
return (int)(ptr2 - KISSBuffer);
|
|
}
|
|
|
|
|
|
void sendAckModeAcks(int snd_ch)
|
|
{
|
|
// format and send any outstanding acks
|
|
|
|
string * temp;
|
|
UCHAR * Msg;
|
|
void * socket;
|
|
|
|
while (KISS_acked[snd_ch].Count)
|
|
{
|
|
UCHAR * ACK = (UCHAR *)malloc(15);
|
|
UCHAR * ackptr = ACK;
|
|
|
|
temp = Strings(&KISS_acked[snd_ch], 0); // get first
|
|
Msg = temp->Data;
|
|
|
|
*ackptr++ = FEND;
|
|
*ackptr++ = Msg[0]; // opcode and channel
|
|
|
|
*ackptr++ = Msg[1];
|
|
*ackptr++ = Msg[2]; // ACK Bytes
|
|
*ackptr++ = FEND;
|
|
|
|
// Socket to reply to is on end
|
|
|
|
Msg += (temp->Length - sizeof(void *));
|
|
|
|
memcpy(&socket, Msg, sizeof(void *));
|
|
|
|
KISSSendtoServer(socket, ACK, 5);
|
|
Delete(&KISS_acked[snd_ch], 0); // This will invalidate temp
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|