forked from KolibriOS/kolibrios
952 lines
20 KiB
C
952 lines
20 KiB
C
|
/*
|
||
|
Copyright (C) 1996-1997 Id Software, Inc.
|
||
|
|
||
|
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 2
|
||
|
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, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
|
||
|
*/
|
||
|
// net_ser.c
|
||
|
|
||
|
#include "quakedef.h"
|
||
|
#include "net_ser.h"
|
||
|
#include "dosisms.h"
|
||
|
#include "crc.h"
|
||
|
|
||
|
#include "net_comx.c"
|
||
|
|
||
|
// serial protocol
|
||
|
|
||
|
#define SERIAL_PROTOCOL_VERSION 3
|
||
|
|
||
|
// The serial protocol is message oriented. The high level message format is
|
||
|
// a one byte message type (MTYPE_xxx), data, and a 16-bit checksum. All
|
||
|
// multi-byte fields are sent in network byte order. There are currently 4
|
||
|
// MTYPEs defined. Their formats are as follows:
|
||
|
//
|
||
|
// MTYPE_RELIABLE sequence data_length data checksum eom
|
||
|
// MTYPE_UNRELIABLE sequence data_length data checksum eom
|
||
|
// MTYPE_ACK sequence checksum eom
|
||
|
// MTYPE_CONTROL data_length data checksum eom
|
||
|
//
|
||
|
// sequence is an 8-bit unsigned value starting from 0
|
||
|
// data_length is a 16-bit unsigned value; it is the length of the data only
|
||
|
// the checksum is a 16-bit value. the CRC formula used is defined in crc.h.
|
||
|
// the checksum covers the entire messages, excluding itself
|
||
|
// eom is a special 2 byte sequence used to mark the End Of Message. This is
|
||
|
// needed for error recovery.
|
||
|
//
|
||
|
// A lot of behavior is based on knowledge of the upper level Quake network
|
||
|
// layer. For example, only one reliable message can be outstanding (pending
|
||
|
// reception of an MTYPE_ACK) at a time.
|
||
|
//
|
||
|
// The low level routines used to communicate with the modem are not part of
|
||
|
// this protocol.
|
||
|
//
|
||
|
// The CONTROL messages are only used for session establishment. They are
|
||
|
// not reliable or sequenced.
|
||
|
|
||
|
#define MTYPE_RELIABLE 0x01
|
||
|
#define MTYPE_UNRELIABLE 0x02
|
||
|
#define MTYPE_CONTROL 0x03
|
||
|
#define MTYPE_ACK 0x04
|
||
|
#define MTYPE_CLIENT 0x80
|
||
|
|
||
|
#define ESCAPE_COMMAND 0xe0
|
||
|
#define ESCAPE_EOM 0x19
|
||
|
|
||
|
static qboolean listening = false;
|
||
|
|
||
|
|
||
|
typedef struct SerialLine_s
|
||
|
{
|
||
|
struct SerialLine_s *next;
|
||
|
qsocket_t *sock;
|
||
|
int lengthStated;
|
||
|
int lengthFound;
|
||
|
int tty;
|
||
|
qboolean connected;
|
||
|
qboolean connecting;
|
||
|
qboolean client;
|
||
|
double connect_time;
|
||
|
unsigned short crcStated;
|
||
|
unsigned short crcValue;
|
||
|
byte currState;
|
||
|
byte prevState;
|
||
|
byte mtype;
|
||
|
byte sequence;
|
||
|
} SerialLine;
|
||
|
|
||
|
#define STATE_READY 0
|
||
|
#define STATE_SEQUENCE 1
|
||
|
#define STATE_LENGTH1 2
|
||
|
#define STATE_LENGTH2 3
|
||
|
#define STATE_DATA 4
|
||
|
#define STATE_CRC1 5
|
||
|
#define STATE_CRC2 6
|
||
|
#define STATE_EOM 7
|
||
|
#define STATE_ESCAPE 8
|
||
|
#define STATE_ABORT 9
|
||
|
|
||
|
SerialLine serialLine[NUM_COM_PORTS];
|
||
|
|
||
|
int myDriverLevel;
|
||
|
|
||
|
static void Serial_SendACK (SerialLine *p, byte sequence);
|
||
|
|
||
|
|
||
|
static void ResetSerialLineProtocol (SerialLine *p)
|
||
|
{
|
||
|
p->connected = false;
|
||
|
p->connecting = false;
|
||
|
p->currState = STATE_READY;
|
||
|
p->prevState = STATE_READY;
|
||
|
p->lengthFound = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int ProcessInQueue(SerialLine *p)
|
||
|
{
|
||
|
int b;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
b = TTY_ReadByte(p->tty);
|
||
|
if (b == ERR_TTY_NODATA)
|
||
|
break;
|
||
|
|
||
|
if (b == ERR_TTY_LINE_STATUS)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
continue;
|
||
|
}
|
||
|
if (b == ERR_TTY_MODEM_STATUS)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
if (p->currState != STATE_ESCAPE)
|
||
|
{
|
||
|
p->prevState = p->currState;
|
||
|
p->currState = STATE_ESCAPE;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (p->currState == STATE_ESCAPE)
|
||
|
{
|
||
|
if (b == ESCAPE_EOM)
|
||
|
{
|
||
|
if (p->prevState == STATE_ABORT)
|
||
|
{
|
||
|
p->currState = STATE_READY;
|
||
|
p->lengthFound = 0;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (p->prevState != STATE_EOM)
|
||
|
{
|
||
|
p->currState = STATE_READY;
|
||
|
p->lengthFound = 0;
|
||
|
Con_DPrintf("Serial: premature EOM\n");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch (p->mtype)
|
||
|
{
|
||
|
case MTYPE_RELIABLE:
|
||
|
Con_DPrintf("Serial: sending ack %u\n", p->sequence);
|
||
|
Serial_SendACK (p, p->sequence);
|
||
|
if (p->sequence == p->sock->receiveSequence)
|
||
|
{
|
||
|
p->sock->receiveSequence = (p->sequence + 1) & 0xff;
|
||
|
p->sock->receiveMessageLength += p->lengthFound;
|
||
|
}
|
||
|
else
|
||
|
Con_DPrintf("Serial: reliable out of order; got %u wanted %u\n", p->sequence, p->sock->receiveSequence);
|
||
|
break;
|
||
|
|
||
|
case MTYPE_UNRELIABLE:
|
||
|
p->sock->unreliableReceiveSequence = (p->sequence + 1) & 0xff;
|
||
|
p->sock->receiveMessageLength += p->lengthFound;
|
||
|
break;
|
||
|
|
||
|
case MTYPE_ACK:
|
||
|
Con_DPrintf("Serial: got ack %u\n", p->sequence);
|
||
|
if (p->sequence == p->sock->sendSequence)
|
||
|
{
|
||
|
p->sock->sendSequence = (p->sock->sendSequence + 1) & 0xff;
|
||
|
p->sock->canSend = true;
|
||
|
}
|
||
|
else
|
||
|
Con_DPrintf("Serial: ack out of order; got %u wanted %u\n",p->sequence, p->sock->sendSequence);
|
||
|
break;
|
||
|
|
||
|
case MTYPE_CONTROL:
|
||
|
p->sock->receiveMessageLength += p->lengthFound;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
p->currState = STATE_READY;
|
||
|
p->lengthFound = 0;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (b != ESCAPE_COMMAND)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: Bad escape sequence\n");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// b == ESCAPE_COMMAND
|
||
|
p->currState = p->prevState;
|
||
|
}
|
||
|
|
||
|
p->prevState = p->currState;
|
||
|
|
||
|
//DEBUG
|
||
|
if (p->sock->receiveMessageLength + p->lengthFound > NET_MAXMESSAGE)
|
||
|
{
|
||
|
Con_DPrintf("Serial blew out receive buffer: %u\n", p->sock->receiveMessageLength + p->lengthFound);
|
||
|
p->currState = STATE_ABORT;
|
||
|
}
|
||
|
if (p->sock->receiveMessageLength + p->lengthFound == NET_MAXMESSAGE)
|
||
|
{
|
||
|
Con_DPrintf("Serial hit receive buffer limit: %u\n", p->sock->receiveMessageLength + p->lengthFound);
|
||
|
p->currState = STATE_ABORT;
|
||
|
}
|
||
|
//end DEBUG
|
||
|
|
||
|
switch (p->currState)
|
||
|
{
|
||
|
case STATE_READY:
|
||
|
CRC_Init(&p->crcValue);
|
||
|
CRC_ProcessByte(&p->crcValue, b);
|
||
|
if (p->client)
|
||
|
{
|
||
|
if ((b & MTYPE_CLIENT) != 0)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: client got own message\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((b & MTYPE_CLIENT) == 0)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: server got own message\n");
|
||
|
break;
|
||
|
}
|
||
|
b &= 0x7f;
|
||
|
}
|
||
|
p->mtype = b;
|
||
|
if (b != MTYPE_CONTROL)
|
||
|
p->currState = STATE_SEQUENCE;
|
||
|
else
|
||
|
p->currState = STATE_LENGTH1;
|
||
|
if (p->mtype < MTYPE_ACK)
|
||
|
{
|
||
|
p->sock->receiveMessage[p->sock->receiveMessageLength] = b;
|
||
|
p->lengthFound++;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STATE_SEQUENCE:
|
||
|
p->sequence = b;
|
||
|
CRC_ProcessByte(&p->crcValue, b);
|
||
|
if (p->mtype != MTYPE_ACK)
|
||
|
p->currState = STATE_LENGTH1;
|
||
|
else
|
||
|
p->currState = STATE_CRC1;
|
||
|
break;
|
||
|
|
||
|
case STATE_LENGTH1:
|
||
|
p->lengthStated = b * 256;
|
||
|
CRC_ProcessByte(&p->crcValue, b);
|
||
|
p->currState = STATE_LENGTH2;
|
||
|
break;
|
||
|
|
||
|
case STATE_LENGTH2:
|
||
|
p->lengthStated += b;
|
||
|
CRC_ProcessByte(&p->crcValue, b);
|
||
|
if (p->mtype == MTYPE_RELIABLE && p->lengthStated > MAX_MSGLEN)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: bad reliable message length %u\n", p->lengthStated);
|
||
|
}
|
||
|
else if (p->mtype == MTYPE_UNRELIABLE && p->lengthStated > MAX_DATAGRAM)
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: bad unreliable message length %u\n", p->lengthStated);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->currState = STATE_DATA;
|
||
|
if (p->mtype < MTYPE_ACK)
|
||
|
{
|
||
|
*(short *)&p->sock->receiveMessage [p->sock->receiveMessageLength + 1] = p->lengthStated;
|
||
|
p->lengthFound += 2;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STATE_DATA:
|
||
|
p->sock->receiveMessage[p->sock->receiveMessageLength + p->lengthFound] = b;
|
||
|
p->lengthFound++;
|
||
|
CRC_ProcessByte(&p->crcValue, b);
|
||
|
if (p->lengthFound == p->lengthStated + 3)
|
||
|
p->currState = STATE_CRC1;
|
||
|
break;
|
||
|
|
||
|
case STATE_CRC1:
|
||
|
p->crcStated = b * 256;
|
||
|
p->currState = STATE_CRC2;
|
||
|
break;
|
||
|
|
||
|
case STATE_CRC2:
|
||
|
p->crcStated += b;
|
||
|
if (p->crcStated == CRC_Value(p->crcValue))
|
||
|
{
|
||
|
p->currState = STATE_EOM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: Bad crc\n");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STATE_EOM:
|
||
|
p->currState = STATE_ABORT;
|
||
|
Con_DPrintf("Serial: Bad message format\n");
|
||
|
break;
|
||
|
|
||
|
case STATE_ABORT:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int Serial_Init (void)
|
||
|
{
|
||
|
int n;
|
||
|
|
||
|
// LATER do Win32 serial support
|
||
|
#ifdef _WIN32
|
||
|
return -1;
|
||
|
#endif
|
||
|
|
||
|
if (COM_CheckParm("-nolan"))
|
||
|
return -1;
|
||
|
if (COM_CheckParm ("-noserial"))
|
||
|
return -1;
|
||
|
|
||
|
myDriverLevel = net_driverlevel;
|
||
|
|
||
|
if (TTY_Init())
|
||
|
return -1;
|
||
|
|
||
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
||
|
{
|
||
|
serialLine[n].tty = TTY_Open(n);
|
||
|
ResetSerialLineProtocol (&serialLine[n]);
|
||
|
}
|
||
|
|
||
|
Con_Printf("Serial driver initialized\n");
|
||
|
serialAvailable = true;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Serial_Shutdown (void)
|
||
|
{
|
||
|
int n;
|
||
|
|
||
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
||
|
{
|
||
|
if (serialLine[n].connected)
|
||
|
Serial_Close(serialLine[n].sock);
|
||
|
}
|
||
|
|
||
|
TTY_Shutdown();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Serial_Listen (qboolean state)
|
||
|
{
|
||
|
listening = state;
|
||
|
}
|
||
|
|
||
|
|
||
|
qboolean Serial_CanSendMessage (qsocket_t *sock)
|
||
|
{
|
||
|
return sock->canSend;
|
||
|
}
|
||
|
|
||
|
|
||
|
qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock)
|
||
|
{
|
||
|
return TTY_OutputQueueIsEmpty(((SerialLine *)sock->driverdata)->tty);
|
||
|
}
|
||
|
|
||
|
|
||
|
int Serial_SendMessage (qsocket_t *sock, sizebuf_t *message)
|
||
|
{
|
||
|
SerialLine *p;
|
||
|
int n;
|
||
|
unsigned short crc;
|
||
|
byte b;
|
||
|
|
||
|
p = (SerialLine *)sock->driverdata;
|
||
|
CRC_Init (&crc);
|
||
|
|
||
|
// message type
|
||
|
b = MTYPE_RELIABLE;
|
||
|
if (p->client)
|
||
|
b |= MTYPE_CLIENT;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// sequence
|
||
|
b = p->sock->sendSequence;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data length
|
||
|
b = message->cursize >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
b = message->cursize & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data
|
||
|
for (n = 0; n < message->cursize; n++)
|
||
|
{
|
||
|
b = message->data[n];
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
}
|
||
|
|
||
|
// checksum
|
||
|
b = CRC_Value (crc) >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
b = CRC_Value (crc) & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
|
||
|
// end of message
|
||
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
||
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
||
|
|
||
|
TTY_Flush(p->tty);
|
||
|
|
||
|
// mark sock as busy and save the message for possible retransmit
|
||
|
sock->canSend = false;
|
||
|
Q_memcpy(sock->sendMessage, message->data, message->cursize);
|
||
|
sock->sendMessageLength = message->cursize;
|
||
|
sock->lastSendTime = net_time;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ReSendMessage (qsocket_t *sock)
|
||
|
{
|
||
|
sizebuf_t temp;
|
||
|
|
||
|
Con_DPrintf("Serial: re-sending reliable\n");
|
||
|
temp.data = sock->sendMessage;
|
||
|
temp.maxsize = sock->sendMessageLength;
|
||
|
temp.cursize = sock->sendMessageLength;
|
||
|
Serial_SendMessage (sock, &temp);
|
||
|
}
|
||
|
|
||
|
|
||
|
int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *message)
|
||
|
{
|
||
|
SerialLine *p;
|
||
|
int n;
|
||
|
unsigned short crc;
|
||
|
byte b;
|
||
|
|
||
|
p = (SerialLine *)sock->driverdata;
|
||
|
|
||
|
if (!TTY_OutputQueueIsEmpty(p->tty))
|
||
|
{
|
||
|
TTY_Flush(p->tty);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
CRC_Init (&crc);
|
||
|
|
||
|
// message type
|
||
|
b = MTYPE_UNRELIABLE;
|
||
|
if (p->client)
|
||
|
b |= MTYPE_CLIENT;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// sequence
|
||
|
b = p->sock->unreliableSendSequence;
|
||
|
p->sock->unreliableSendSequence = (b + 1) & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data length
|
||
|
b = message->cursize >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
b = message->cursize & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data
|
||
|
for (n = 0; n < message->cursize; n++)
|
||
|
{
|
||
|
b = message->data[n];
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
}
|
||
|
|
||
|
// checksum
|
||
|
b = CRC_Value (crc) >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
b = CRC_Value (crc) & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
|
||
|
// end of message
|
||
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
||
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
||
|
|
||
|
TTY_Flush(p->tty);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void Serial_SendACK (SerialLine *p, byte sequence)
|
||
|
{
|
||
|
unsigned short crc;
|
||
|
byte b;
|
||
|
|
||
|
CRC_Init (&crc);
|
||
|
|
||
|
// message type
|
||
|
b = MTYPE_ACK;
|
||
|
if (p->client)
|
||
|
b |= MTYPE_CLIENT;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// sequence
|
||
|
b = sequence;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// checksum
|
||
|
b = CRC_Value (crc) >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
b = CRC_Value (crc) & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
|
||
|
// end of message
|
||
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
||
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
||
|
|
||
|
TTY_Flush(p->tty);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void Serial_SendControlMessage (SerialLine *p, sizebuf_t *message)
|
||
|
{
|
||
|
unsigned short crc;
|
||
|
int n;
|
||
|
byte b;
|
||
|
|
||
|
CRC_Init (&crc);
|
||
|
|
||
|
// message type
|
||
|
b = MTYPE_CONTROL;
|
||
|
if (p->client)
|
||
|
b |= MTYPE_CLIENT;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data length
|
||
|
b = message->cursize >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
b = message->cursize & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
|
||
|
// data
|
||
|
for (n = 0; n < message->cursize; n++)
|
||
|
{
|
||
|
b = message->data[n];
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
CRC_ProcessByte (&crc, b);
|
||
|
}
|
||
|
|
||
|
// checksum
|
||
|
b = CRC_Value (crc) >> 8;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
b = CRC_Value (crc) & 0xff;
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
if (b == ESCAPE_COMMAND)
|
||
|
TTY_WriteByte(p->tty, b);
|
||
|
|
||
|
// end of message
|
||
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
||
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
||
|
|
||
|
TTY_Flush(p->tty);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int _Serial_GetMessage (SerialLine *p)
|
||
|
{
|
||
|
byte ret;
|
||
|
short length;
|
||
|
|
||
|
if (ProcessInQueue(p))
|
||
|
return -1;
|
||
|
|
||
|
if (p->sock->receiveMessageLength == 0)
|
||
|
return 0;
|
||
|
|
||
|
ret = p->sock->receiveMessage[0];
|
||
|
length = *(short *)&p->sock->receiveMessage[1];
|
||
|
if (ret == MTYPE_CONTROL)
|
||
|
ret = 1;
|
||
|
|
||
|
SZ_Clear (&net_message);
|
||
|
SZ_Write (&net_message, &p->sock->receiveMessage[3], length);
|
||
|
|
||
|
length += 3;
|
||
|
p->sock->receiveMessageLength -= length;
|
||
|
|
||
|
if (p->sock->receiveMessageLength + p->lengthFound)
|
||
|
Q_memcpy(p->sock->receiveMessage, &p->sock->receiveMessage[length], p->sock->receiveMessageLength + p->lengthFound);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int Serial_GetMessage (qsocket_t *sock)
|
||
|
{
|
||
|
SerialLine *p;
|
||
|
int ret;
|
||
|
|
||
|
p = (SerialLine *)sock->driverdata;
|
||
|
|
||
|
ret = _Serial_GetMessage (p);
|
||
|
|
||
|
if (ret == 1)
|
||
|
messagesReceived++;
|
||
|
|
||
|
if (!sock->canSend)
|
||
|
if ((net_time - sock->lastSendTime) > 1.0)
|
||
|
{
|
||
|
ReSendMessage (sock);
|
||
|
sock->lastSendTime = net_time;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Serial_Close (qsocket_t *sock)
|
||
|
{
|
||
|
SerialLine *p = (SerialLine *)sock->driverdata;
|
||
|
TTY_Close(p->tty);
|
||
|
ResetSerialLineProtocol (p);
|
||
|
}
|
||
|
|
||
|
|
||
|
char *com_types[] = {"direct", "modem"};
|
||
|
unsigned com_bauds[] = {9600, 14400, 19200, 28800, 57600};
|
||
|
|
||
|
void Serial_SearchForHosts (qboolean xmit)
|
||
|
{
|
||
|
int n;
|
||
|
SerialLine *p;
|
||
|
|
||
|
if (sv.active)
|
||
|
return;
|
||
|
|
||
|
if (hostCacheCount == HOSTCACHESIZE)
|
||
|
return;
|
||
|
|
||
|
// see if we've already answered
|
||
|
for (n = 0; n < hostCacheCount; n++)
|
||
|
if (Q_strcmp (hostcache[n].cname, "#") == 0)
|
||
|
return;
|
||
|
|
||
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
||
|
if (TTY_IsEnabled(n))
|
||
|
break;
|
||
|
if (n == NUM_COM_PORTS)
|
||
|
return;
|
||
|
p = &serialLine[n];
|
||
|
|
||
|
if (TTY_IsModem(p->tty))
|
||
|
return;
|
||
|
|
||
|
sprintf(hostcache[hostCacheCount].name, "COM%u", n+1);
|
||
|
Q_strcpy(hostcache[hostCacheCount].map, "");
|
||
|
hostcache[hostCacheCount].users = 0;
|
||
|
hostcache[hostCacheCount].maxusers = 0;
|
||
|
hostcache[hostCacheCount].driver = net_driverlevel;
|
||
|
Q_strcpy(hostcache[hostCacheCount].cname, "#");
|
||
|
hostCacheCount++;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
static qsocket_t *_Serial_Connect (char *host, SerialLine *p)
|
||
|
{
|
||
|
int ret;
|
||
|
double start_time;
|
||
|
double last_time;
|
||
|
|
||
|
p->client = true;
|
||
|
if (TTY_Connect(p->tty, host))
|
||
|
return NULL;
|
||
|
|
||
|
p->sock = NET_NewQSocket ();
|
||
|
p->sock->driver = myDriverLevel;
|
||
|
if (p->sock == NULL)
|
||
|
{
|
||
|
Con_Printf("No sockets available\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
p->sock->driverdata = p;
|
||
|
|
||
|
// send the connection request
|
||
|
start_time = SetNetTime();
|
||
|
last_time = 0.0;
|
||
|
|
||
|
SZ_Clear(&net_message);
|
||
|
MSG_WriteByte(&net_message, CCREQ_CONNECT);
|
||
|
MSG_WriteString(&net_message, "QUAKE");
|
||
|
do
|
||
|
{
|
||
|
SetNetTime();
|
||
|
if ((net_time - last_time) >= 1.0)
|
||
|
{
|
||
|
Serial_SendControlMessage (p, &net_message);
|
||
|
last_time = net_time;
|
||
|
Con_Printf("trying...\n"); SCR_UpdateScreen ();
|
||
|
}
|
||
|
ret = _Serial_GetMessage (p);
|
||
|
}
|
||
|
while (ret == 0 && (net_time - start_time) < 5.0);
|
||
|
|
||
|
if (ret == 0)
|
||
|
{
|
||
|
Con_Printf("Unable to connect, no response\n");
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
if (ret == -1)
|
||
|
{
|
||
|
Con_Printf("Connection request error\n");
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
MSG_BeginReading ();
|
||
|
ret = MSG_ReadByte();
|
||
|
if (ret == CCREP_REJECT)
|
||
|
{
|
||
|
Con_Printf(MSG_ReadString());
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
if (ret != CCREP_ACCEPT)
|
||
|
{
|
||
|
Con_Printf("Unknown connection response\n");
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
p->connected = true;
|
||
|
p->sock->lastMessageTime = net_time;
|
||
|
|
||
|
Con_Printf ("Connection accepted\n");
|
||
|
|
||
|
return p->sock;
|
||
|
|
||
|
ErrorReturn:
|
||
|
TTY_Disconnect(p->tty);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
qsocket_t *Serial_Connect (char *host)
|
||
|
{
|
||
|
int n;
|
||
|
qsocket_t *ret = NULL;
|
||
|
|
||
|
// see if this looks like a phone number
|
||
|
if (*host == '#')
|
||
|
host++;
|
||
|
for (n = 0; n < Q_strlen(host); n++)
|
||
|
if (host[n] == '.' || host[n] == ':')
|
||
|
return NULL;
|
||
|
|
||
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
||
|
if (TTY_IsEnabled(n) && !serialLine[n].connected)
|
||
|
if ((ret = _Serial_Connect (host, &serialLine[n])))
|
||
|
break;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
static qsocket_t *_Serial_CheckNewConnections (SerialLine *p)
|
||
|
{
|
||
|
int command;
|
||
|
|
||
|
p->client = false;
|
||
|
if (!TTY_CheckForConnection(p->tty))
|
||
|
return NULL;
|
||
|
|
||
|
if (TTY_IsModem(p->tty))
|
||
|
{
|
||
|
if (!p->connecting)
|
||
|
{
|
||
|
p->connecting = true;
|
||
|
p->connect_time = net_time;
|
||
|
}
|
||
|
else if ((net_time - p->connect_time) > 15.0)
|
||
|
{
|
||
|
p->connecting = false;
|
||
|
TTY_Disconnect(p->tty);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p->sock = NET_NewQSocket ();
|
||
|
p->sock->driver = myDriverLevel;
|
||
|
if (p->sock == NULL)
|
||
|
{
|
||
|
Con_Printf("No sockets available\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
p->sock->driverdata = p;
|
||
|
|
||
|
SZ_Clear(&net_message);
|
||
|
if (_Serial_GetMessage(p) != 1)
|
||
|
{
|
||
|
NET_FreeQSocket(p->sock);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
MSG_BeginReading ();
|
||
|
command = MSG_ReadByte();
|
||
|
|
||
|
if (command == CCREQ_SERVER_INFO)
|
||
|
{
|
||
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
||
|
return NULL;
|
||
|
|
||
|
if (MSG_ReadByte() != SERIAL_PROTOCOL_VERSION)
|
||
|
return NULL;
|
||
|
|
||
|
SZ_Clear(&net_message);
|
||
|
MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
|
||
|
MSG_WriteString(&net_message, hostname.string);
|
||
|
MSG_WriteString(&net_message, sv.name);
|
||
|
MSG_WriteByte(&net_message, net_activeconnections);
|
||
|
MSG_WriteByte(&net_message, svs.maxclients);
|
||
|
Serial_SendControlMessage (p, &net_message);
|
||
|
SZ_Clear(&net_message);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (command != CCREQ_CONNECT)
|
||
|
return NULL;
|
||
|
|
||
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
||
|
return NULL;
|
||
|
|
||
|
// send him back the info about the server connection he has been allocated
|
||
|
SZ_Clear(&net_message);
|
||
|
MSG_WriteByte(&net_message, CCREP_ACCEPT);
|
||
|
Serial_SendControlMessage (p, &net_message);
|
||
|
SZ_Clear(&net_message);
|
||
|
|
||
|
p->connected = true;
|
||
|
p->connecting = false;
|
||
|
p->sock->lastMessageTime = net_time;
|
||
|
sprintf(p->sock->address, "COM%u", (int)((p - serialLine) + 1));
|
||
|
|
||
|
return p->sock;
|
||
|
}
|
||
|
|
||
|
qsocket_t *Serial_CheckNewConnections (void)
|
||
|
{
|
||
|
int n;
|
||
|
qsocket_t *ret = NULL;
|
||
|
|
||
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
||
|
if (TTY_IsEnabled(n) && !serialLine[n].connected)
|
||
|
if ((ret = _Serial_CheckNewConnections (&serialLine[n])))
|
||
|
break;
|
||
|
return ret;
|
||
|
}
|