mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 14:42:07 +00:00
1261 lines
29 KiB
C
1261 lines
29 KiB
C
/*
|
|
net_comx.c
|
|
|
|
@description@
|
|
|
|
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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <dos.h>
|
|
#include <dpmi.h>
|
|
|
|
#define NUM_COM_PORTS 2
|
|
|
|
#define ERR_TTY_LINE_STATUS -1
|
|
#define ERR_TTY_MODEM_STATUS -2
|
|
#define ERR_TTY_NODATA -3
|
|
|
|
#define QUEUESIZE 8192
|
|
#define QUEUEMASK (QUEUESIZE - 1)
|
|
|
|
typedef struct {
|
|
volatile int head;
|
|
volatile int tail;
|
|
volatile byte data[QUEUESIZE];
|
|
} queue;
|
|
|
|
#define FULL(q) (q.head == ((q.tail-1) & QUEUEMASK))
|
|
#define EMPTY(q) (q.tail == q.head)
|
|
#define ENQUEUE(q,b) (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK)
|
|
#define DEQUEUE(q,b) (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK)
|
|
|
|
extern int m_return_state;
|
|
extern int m_state;
|
|
extern qboolean m_return_onerror;
|
|
extern char m_return_reason[32];
|
|
|
|
// 8250, 16550 definitions
|
|
#define TRANSMIT_HOLDING_REGISTER 0x00
|
|
#define RECEIVE_BUFFER_REGISTER 0x00
|
|
#define INTERRUPT_ENABLE_REGISTER 0x01
|
|
#define IER_RX_DATA_READY 0x01
|
|
#define IER_TX_HOLDING_REGISTER_EMPTY 0x02
|
|
#define IER_LINE_STATUS 0x04
|
|
#define IER_MODEM_STATUS 0x08
|
|
#define INTERRUPT_ID_REGISTER 0x02
|
|
#define IIR_MODEM_STATUS_INTERRUPT 0x00
|
|
#define IIR_TX_HOLDING_REGISTER_INTERRUPT 0x02
|
|
#define IIR_RX_DATA_READY_INTERRUPT 0x04
|
|
#define IIR_LINE_STATUS_INTERRUPT 0x06
|
|
#define IIR_FIFO_TIMEOUT 0x0c
|
|
#define IIR_FIFO_ENABLED 0xc0
|
|
#define FIFO_CONTROL_REGISTER 0x02
|
|
#define FCR_FIFO_ENABLE 0x01
|
|
#define FCR_RCVR_FIFO_RESET 0x02
|
|
#define FCR_XMIT_FIFO_RESET 0x04
|
|
#define FCR_TRIGGER_01 0x00
|
|
#define FCR_TRIGGER_04 0x40
|
|
#define FCR_TRIGGER_08 0x80
|
|
#define FCR_TRIGGER_16 0xc0
|
|
#define LINE_CONTROL_REGISTER 0x03
|
|
#define LCR_DATA_BITS_5 0x00
|
|
#define LCR_DATA_BITS_6 0x01
|
|
#define LCR_DATA_BITS_7 0x02
|
|
#define LCR_DATA_BITS_8 0x03
|
|
#define LCR_STOP_BITS_1 0x00
|
|
#define LCR_STOP_BITS_2 0x04
|
|
#define LCR_PARITY_NONE 0x00
|
|
#define LCR_PARITY_ODD 0x08
|
|
#define LCR_PARITY_EVEN 0x18
|
|
#define LCR_PARITY_MARK 0x28
|
|
#define LCR_PARITY_SPACE 0x38
|
|
#define LCR_SET_BREAK 0x40
|
|
#define LCR_DLAB 0x80
|
|
#define MODEM_CONTROL_REGISTER 0x04
|
|
#define MCR_DTR 0x01
|
|
#define MCR_RTS 0x02
|
|
#define MCR_OUT1 0x04
|
|
#define MCR_OUT2 0x08
|
|
#define MCR_LOOPBACK 0x10
|
|
#define LINE_STATUS_REGISTER 0x05
|
|
#define LSR_DATA_READY 0x01
|
|
#define LSR_OVERRUN_ERROR 0x02
|
|
#define LSR_PARITY_ERROR 0x04
|
|
#define LSR_FRAMING_ERROR 0x08
|
|
#define LSR_BREAK_DETECT 0x10
|
|
#define LSR_TRANSMITTER_BUFFER_EMPTY 0x20
|
|
#define LSR_TRANSMITTER_EMPTY 0x40
|
|
#define LSR_FIFO_DIRTY 0x80
|
|
#define MODEM_STATUS_REGISTER 0x06
|
|
#define MSR_DELTA_CTS 0x01
|
|
#define MSR_DELTA_DSR 0x02
|
|
#define MSR_DELTA_RI 0x04
|
|
#define MSR_DELTA_CD 0x08
|
|
#define MSR_CTS 0x10
|
|
#define MSR_DSR 0x20
|
|
#define MSR_RI 0x40
|
|
#define MSR_CD 0x80
|
|
#define DIVISOR_LATCH_LOW 0x00
|
|
#define DIVISOR_LATCH_HIGH 0x01
|
|
|
|
#define MODEM_STATUS_MASK (MSR_CTS | MSR_DSR | MSR_CD)
|
|
|
|
#define UART_AUTO 0
|
|
#define UART_8250 1
|
|
#define UART_16550 2
|
|
|
|
static int ISA_uarts[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
|
static int ISA_IRQs[] = { 4, 3, 4, 3 };
|
|
|
|
typedef struct ComPort_s {
|
|
struct ComPort_s *next;
|
|
_go32_dpmi_seginfo protectedModeInfo;
|
|
_go32_dpmi_seginfo protectedModeSaveInfo;
|
|
int uart;
|
|
volatile byte modemStatus;
|
|
byte modemStatusIgnore;
|
|
byte lineStatus;
|
|
byte bufferUsed;
|
|
qboolean enabled;
|
|
volatile qboolean statusUpdated;
|
|
qboolean useModem;
|
|
qboolean modemInitialized;
|
|
qboolean modemRang;
|
|
qboolean modemConnected;
|
|
queue inputQueue;
|
|
queue outputQueue;
|
|
char clear[16];
|
|
char startup[32];
|
|
char shutdown[16];
|
|
char buffer[128];
|
|
PollProcedure poll;
|
|
double timestamp;
|
|
byte uartType;
|
|
byte irq;
|
|
byte baudBits;
|
|
byte lineControl;
|
|
byte portNumber;
|
|
char dialType;
|
|
char name[4];
|
|
} ComPort;
|
|
|
|
ComPort *portList = NULL;
|
|
ComPort *handleToPort[NUM_COM_PORTS];
|
|
|
|
static int Modem_Command (ComPort * p, char *commandString);
|
|
static char *Modem_Response (ComPort * p);
|
|
static void Modem_Hangup (ComPort * p);
|
|
|
|
int TTY_Init (void);
|
|
void TTY_Shutdown (void);
|
|
int TTY_Open (int serialPortNumber);
|
|
void TTY_Close (int handle);
|
|
int TTY_ReadByte (int handle);
|
|
int TTY_WriteByte (int handle, byte data);
|
|
void TTY_Flush (int handle);
|
|
int TTY_Connect (int handle, char *host);
|
|
void TTY_Disconnect (int handle);
|
|
qboolean TTY_CheckForConnection (int handle);
|
|
qboolean TTY_IsEnabled (int serialPortNumber);
|
|
qboolean TTY_IsModem (int serialPortNumber);
|
|
qboolean TTY_OutputQueueIsEmpty (int handle);
|
|
|
|
static void
|
|
ISR_8250 (ComPort * p)
|
|
{
|
|
byte source = 0;
|
|
byte b;
|
|
|
|
disable ();
|
|
|
|
while ((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) {
|
|
switch (source) {
|
|
case IIR_RX_DATA_READY_INTERRUPT:
|
|
b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
|
|
if (!FULL (p->inputQueue)) {
|
|
ENQUEUE (p->inputQueue, b);
|
|
} else {
|
|
p->lineStatus |= LSR_OVERRUN_ERROR;
|
|
p->statusUpdated = true;
|
|
}
|
|
break;
|
|
|
|
case IIR_TX_HOLDING_REGISTER_INTERRUPT:
|
|
if (!EMPTY (p->outputQueue)) {
|
|
DEQUEUE (p->outputQueue, b);
|
|
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
|
|
}
|
|
break;
|
|
|
|
case IIR_MODEM_STATUS_INTERRUPT:
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK)
|
|
| p->modemStatusIgnore;
|
|
p->statusUpdated = true;
|
|
break;
|
|
|
|
case IIR_LINE_STATUS_INTERRUPT:
|
|
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
|
|
p->statusUpdated = true;
|
|
break;
|
|
}
|
|
source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
|
|
}
|
|
outportb (0x20, 0x20);
|
|
}
|
|
|
|
static void
|
|
COM1_ISR_8250 (void)
|
|
{
|
|
ISR_8250 (handleToPort[0]);
|
|
}
|
|
|
|
static void
|
|
COM2_ISR_8250 (void)
|
|
{
|
|
ISR_8250 (handleToPort[1]);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
ISR_16550 (ComPort * p)
|
|
{
|
|
int count;
|
|
byte source;
|
|
byte b;
|
|
|
|
disable ();
|
|
while ((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) {
|
|
switch (source) {
|
|
case IIR_RX_DATA_READY_INTERRUPT:
|
|
do {
|
|
b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
|
|
if (!FULL (p->inputQueue)) {
|
|
ENQUEUE (p->inputQueue, b);
|
|
} else {
|
|
p->lineStatus |= LSR_OVERRUN_ERROR;
|
|
p->statusUpdated = true;
|
|
}
|
|
} while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY);
|
|
break;
|
|
|
|
case IIR_TX_HOLDING_REGISTER_INTERRUPT:
|
|
count = 16;
|
|
while ((!EMPTY (p->outputQueue)) && count--) {
|
|
DEQUEUE (p->outputQueue, b);
|
|
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
|
|
}
|
|
break;
|
|
|
|
case IIR_MODEM_STATUS_INTERRUPT:
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK)
|
|
| p->modemStatusIgnore;
|
|
p->statusUpdated = true;
|
|
break;
|
|
|
|
case IIR_LINE_STATUS_INTERRUPT:
|
|
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
|
|
p->statusUpdated = true;
|
|
break;
|
|
}
|
|
source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
|
|
}
|
|
|
|
// check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a!
|
|
if (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_TRANSMITTER_EMPTY) {
|
|
count = 16;
|
|
while ((!EMPTY (p->outputQueue)) && count--) {
|
|
DEQUEUE (p->outputQueue, b);
|
|
outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
|
|
}
|
|
}
|
|
|
|
outportb (0x20, 0x20);
|
|
}
|
|
|
|
static void
|
|
COM1_ISR_16550 (void)
|
|
{
|
|
ISR_16550 (handleToPort[0]);
|
|
}
|
|
|
|
static void
|
|
COM2_ISR_16550 (void)
|
|
{
|
|
ISR_16550 (handleToPort[1]);
|
|
}
|
|
|
|
|
|
void
|
|
TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud,
|
|
qboolean *useModem)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[portNumber];
|
|
*port = p->uart;
|
|
*irq = p->irq;
|
|
*baud = 115200 / p->baudBits;
|
|
*useModem = p->useModem;
|
|
}
|
|
|
|
void
|
|
TTY_SetComPortConfig (int portNumber, int port, int irq, int baud,
|
|
qboolean useModem)
|
|
{
|
|
ComPort *p;
|
|
float temp;
|
|
|
|
if (useModem) {
|
|
if (baud == 14400)
|
|
baud = 19200;
|
|
if (baud == 28800)
|
|
baud = 38400;
|
|
}
|
|
|
|
p = handleToPort[portNumber];
|
|
p->uart = port;
|
|
p->irq = irq;
|
|
p->baudBits = 115200 / baud;
|
|
p->useModem = useModem;
|
|
|
|
if (useModem)
|
|
temp = 1.0;
|
|
else
|
|
temp = 0.0;
|
|
|
|
Cvar_SetValue ("_config_com_port", (float) port);
|
|
Cvar_SetValue ("_config_com_irq", (float) irq);
|
|
Cvar_SetValue ("_config_com_baud", (float) baud);
|
|
Cvar_SetValue ("_config_com_modem", temp);
|
|
}
|
|
|
|
void
|
|
TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init,
|
|
char *hangup)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[portNumber];
|
|
*dialType = p->dialType;
|
|
Q_strcpy (clear, p->clear);
|
|
Q_strcpy (init, p->startup);
|
|
Q_strcpy (hangup, p->shutdown);
|
|
}
|
|
|
|
void
|
|
TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init,
|
|
char *hangup)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[portNumber];
|
|
p->dialType = dialType[0];
|
|
Q_strcpy (p->clear, clear);
|
|
Q_strcpy (p->startup, init);
|
|
Q_strcpy (p->shutdown, hangup);
|
|
|
|
p->modemInitialized = false;
|
|
|
|
Cvar_Set ("_config_modem_dialtype", dialType);
|
|
Cvar_Set ("_config_modem_clear", clear);
|
|
Cvar_Set ("_config_modem_init", init);
|
|
Cvar_Set ("_config_modem_hangup", hangup);
|
|
}
|
|
|
|
|
|
static void
|
|
ResetComPortConfig (ComPort * p)
|
|
{
|
|
p->useModem = false;
|
|
p->uartType = UART_AUTO;
|
|
p->uart = ISA_uarts[p->portNumber];
|
|
p->irq = ISA_IRQs[p->portNumber];
|
|
p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR;
|
|
p->baudBits = 115200 / 57600;
|
|
p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE;
|
|
Q_strcpy (p->clear, "ATZ");
|
|
Q_strcpy (p->startup, "");
|
|
Q_strcpy (p->shutdown, "AT H");
|
|
p->modemRang = false;
|
|
p->modemConnected = false;
|
|
p->statusUpdated = false;
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
ComPort_Enable (ComPort * p)
|
|
{
|
|
void (*isr) (void);
|
|
int n;
|
|
byte b;
|
|
|
|
if (p->enabled) {
|
|
Con_Printf ("Already enabled\n");
|
|
return;
|
|
}
|
|
// disable all UART interrupts
|
|
outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
|
|
|
|
// clear out any buffered uncoming data
|
|
while ((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY)
|
|
inportb (p->uart + RECEIVE_BUFFER_REGISTER);
|
|
|
|
// get the current line and modem status
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) |
|
|
p->modemStatusIgnore;
|
|
p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
|
|
|
|
// clear any UART interrupts
|
|
do {
|
|
n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7;
|
|
if (n == IIR_RX_DATA_READY_INTERRUPT)
|
|
inportb (p->uart + RECEIVE_BUFFER_REGISTER);
|
|
} while (!(n & 1));
|
|
|
|
if (p->uartType == UART_AUTO) {
|
|
outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE);
|
|
b = inportb (p->uart + INTERRUPT_ID_REGISTER);
|
|
if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED)
|
|
p->uartType = UART_16550;
|
|
else
|
|
p->uartType = UART_8250;
|
|
}
|
|
// save the old interrupt handler
|
|
_go32_dpmi_get_protected_mode_interrupt_vector (p->irq + 8,
|
|
&p->protectedModeSaveInfo);
|
|
|
|
if (p->uartType == UART_8250) {
|
|
outportb (p->uart + FIFO_CONTROL_REGISTER, 0);
|
|
if (p == handleToPort[0])
|
|
isr = COM1_ISR_8250;
|
|
else
|
|
isr = COM2_ISR_8250;
|
|
} else {
|
|
outportb (p->uart + FIFO_CONTROL_REGISTER,
|
|
FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET |
|
|
FCR_TRIGGER_08);
|
|
if (p == handleToPort[0])
|
|
isr = COM1_ISR_16550;
|
|
else
|
|
isr = COM2_ISR_16550;
|
|
}
|
|
|
|
p->protectedModeInfo.pm_offset = (int) isr;
|
|
|
|
n = _go32_dpmi_allocate_iret_wrapper (&p->protectedModeInfo);
|
|
if (n) {
|
|
Con_Printf ("serial: protected mode callback allocation failed\n");
|
|
return;
|
|
}
|
|
// disable interrupts at the processor
|
|
disable ();
|
|
|
|
// install our interrupt handlers now
|
|
_go32_dpmi_set_protected_mode_interrupt_vector (p->irq + 8,
|
|
&p->protectedModeInfo);
|
|
|
|
// enable our interrupt at the PIC
|
|
outportb (0x21, inportb (0x21) & ~(1 << p->irq));
|
|
|
|
// enable interrupts at the processor
|
|
enable ();
|
|
|
|
// enable interrupts at the PIC
|
|
outportb (0x20, 0xc2);
|
|
|
|
// set baud rate & line control
|
|
outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl);
|
|
outportb (p->uart, p->baudBits);
|
|
outportb (p->uart + 1, 0);
|
|
outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl);
|
|
|
|
// set modem control register & enable uart interrupt generation
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
|
|
|
|
// enable the individual interrupts at the uart
|
|
outportb (p->uart + INTERRUPT_ENABLE_REGISTER,
|
|
IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY |
|
|
IER_LINE_STATUS | IER_MODEM_STATUS);
|
|
|
|
p->enabled = true;
|
|
}
|
|
|
|
|
|
static void
|
|
ComPort_Disable (ComPort * p)
|
|
{
|
|
if (!p->enabled) {
|
|
Con_Printf ("Already disabled\n");
|
|
return;
|
|
}
|
|
// disable interrupts at the uart
|
|
outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
|
|
|
|
// disable our interrupt at the PIC
|
|
outportb (0x21, inportb (0x21) | (1 << p->irq));
|
|
|
|
// disable interrupts at the processor
|
|
disable ();
|
|
|
|
// restore the old interrupt handler
|
|
_go32_dpmi_set_protected_mode_interrupt_vector (p->irq + 8,
|
|
&p->protectedModeSaveInfo);
|
|
_go32_dpmi_free_iret_wrapper (&p->protectedModeInfo);
|
|
|
|
// enable interrupts at the processor
|
|
enable ();
|
|
|
|
p->enabled = false;
|
|
}
|
|
|
|
|
|
static int
|
|
CheckStatus (ComPort * p)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (p->statusUpdated) {
|
|
p->statusUpdated = false;
|
|
|
|
if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR |
|
|
LSR_FRAMING_ERROR | LSR_BREAK_DETECT)) {
|
|
if (p->lineStatus & LSR_OVERRUN_ERROR)
|
|
Con_DPrintf ("Serial overrun error\n");
|
|
if (p->lineStatus & LSR_PARITY_ERROR)
|
|
Con_DPrintf ("Serial parity error\n");
|
|
if (p->lineStatus & LSR_FRAMING_ERROR)
|
|
Con_DPrintf ("Serial framing error\n");
|
|
if (p->lineStatus & LSR_BREAK_DETECT)
|
|
Con_DPrintf ("Serial break detect\n");
|
|
ret = ERR_TTY_LINE_STATUS;
|
|
}
|
|
|
|
if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) {
|
|
if (!(p->modemStatus & MSR_CTS))
|
|
Con_Printf ("Serial lost CTS\n");
|
|
if (!(p->modemStatus & MSR_DSR))
|
|
Con_Printf ("Serial lost DSR\n");
|
|
if (!(p->modemStatus & MSR_CD))
|
|
Con_Printf ("Serial lost Carrier\n");
|
|
ret = ERR_TTY_MODEM_STATUS;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void
|
|
Modem_Init (ComPort * p)
|
|
{
|
|
double start;
|
|
char *response;
|
|
|
|
Con_Printf ("Initializing modem...\n");
|
|
|
|
// write 0 to MCR, wait 1/2 sec, then write the real value back again
|
|
// I got this from the guys at head-to-head who say it's necessary.
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER, 0);
|
|
start = Sys_DoubleTime ();
|
|
while ((Sys_DoubleTime () - start) < 0.5);
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
|
|
start = Sys_DoubleTime ();
|
|
while ((Sys_DoubleTime () - start) < 0.25);
|
|
|
|
if (*p->clear) {
|
|
Modem_Command (p, p->clear);
|
|
start = Sys_DoubleTime ();
|
|
while (1) {
|
|
if ((Sys_DoubleTime () - start) > 3.0) {
|
|
Con_Printf ("No response - clear failed\n");
|
|
p->enabled = false;
|
|
goto failed;
|
|
}
|
|
response = Modem_Response (p);
|
|
if (!response)
|
|
continue;
|
|
if (Q_strncmp (response, "OK", 2) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "ERROR", 5) == 0) {
|
|
p->enabled = false;
|
|
goto failed;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*p->startup) {
|
|
Modem_Command (p, p->startup);
|
|
start = Sys_DoubleTime ();
|
|
while (1) {
|
|
if ((Sys_DoubleTime () - start) > 3.0) {
|
|
Con_Printf ("No response - init failed\n");
|
|
p->enabled = false;
|
|
goto failed;
|
|
}
|
|
response = Modem_Response (p);
|
|
if (!response)
|
|
continue;
|
|
if (Q_strncmp (response, "OK", 2) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "ERROR", 5) == 0) {
|
|
p->enabled = false;
|
|
goto failed;
|
|
}
|
|
}
|
|
}
|
|
|
|
p->modemInitialized = true;
|
|
return;
|
|
|
|
failed:
|
|
if (m_return_onerror) {
|
|
key_dest = key_menu;
|
|
m_state = m_return_state;
|
|
m_return_onerror = false;
|
|
Q_strcpy (m_return_reason, "Initialization Failed");
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
TTY_Enable (int handle)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
if (p->enabled)
|
|
return;
|
|
|
|
ComPort_Enable (p);
|
|
|
|
if (p->useModem && !p->modemInitialized)
|
|
Modem_Init (p);
|
|
}
|
|
|
|
|
|
int
|
|
TTY_Open (int serialPortNumber)
|
|
{
|
|
return serialPortNumber;
|
|
}
|
|
|
|
|
|
void
|
|
TTY_Close (int handle)
|
|
{
|
|
ComPort *p;
|
|
double startTime;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
startTime = Sys_DoubleTime ();
|
|
while ((Sys_DoubleTime () - startTime) < 1.0)
|
|
if (EMPTY (p->outputQueue))
|
|
break;
|
|
|
|
if (p->useModem) {
|
|
if (p->modemConnected)
|
|
Modem_Hangup (p);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
TTY_ReadByte (int handle)
|
|
{
|
|
int ret;
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
if ((ret = CheckStatus (p)) != 0)
|
|
return ret;
|
|
|
|
if (EMPTY (p->inputQueue))
|
|
return ERR_TTY_NODATA;
|
|
|
|
DEQUEUE (p->inputQueue, ret);
|
|
return (ret & 0xff);
|
|
}
|
|
|
|
|
|
int
|
|
TTY_WriteByte (int handle, byte data)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
if (FULL (p->outputQueue))
|
|
return -1;
|
|
|
|
ENQUEUE (p->outputQueue, data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
TTY_Flush (int handle)
|
|
{
|
|
byte b;
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
if (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_TRANSMITTER_EMPTY) {
|
|
DEQUEUE (p->outputQueue, b);
|
|
outportb (p->uart, b);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
TTY_Connect (int handle, char *host)
|
|
{
|
|
double start;
|
|
ComPort *p;
|
|
char *response = NULL;
|
|
keydest_t save_key_dest;
|
|
byte dialstring[64];
|
|
byte b;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) {
|
|
Con_Printf ("Serial: line not ready (");
|
|
if ((p->modemStatus & MSR_CTS) == 0)
|
|
Con_Printf (" CTS");
|
|
if ((p->modemStatus & MSR_DSR) == 0)
|
|
Con_Printf (" DSR");
|
|
if ((p->modemStatus & MSR_CD) == 0)
|
|
Con_Printf (" CD");
|
|
Con_Printf (" )");
|
|
return -1;
|
|
}
|
|
// discard any scraps in the input buffer
|
|
while (!EMPTY (p->inputQueue))
|
|
DEQUEUE (p->inputQueue, b);
|
|
|
|
CheckStatus (p);
|
|
|
|
if (p->useModem) {
|
|
save_key_dest = key_dest;
|
|
key_dest = key_console;
|
|
key_count = -2;
|
|
|
|
Con_Printf ("Dialing...\n");
|
|
snprintf (dialstring, sizeof (dialstring), "AT D%c %s\r", p->dialType,
|
|
host);
|
|
Modem_Command (p, dialstring);
|
|
start = Sys_DoubleTime ();
|
|
while (1) {
|
|
if ((Sys_DoubleTime () - start) > 60.0) {
|
|
Con_Printf ("Dialing failure!\n");
|
|
break;
|
|
}
|
|
|
|
IN_SendKeyEvents ();
|
|
if (key_count == 0) {
|
|
if (key_lastpress != K_ESCAPE) {
|
|
key_count = -2;
|
|
continue;
|
|
}
|
|
Con_Printf ("Aborting...\n");
|
|
while ((Sys_DoubleTime () - start) < 5.0);
|
|
disable ();
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER,
|
|
inportb (p->uart +
|
|
MODEM_CONTROL_REGISTER) & ~MCR_DTR);
|
|
enable ();
|
|
start = Sys_DoubleTime ();
|
|
while ((Sys_DoubleTime () - start) < 0.75);
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER,
|
|
inportb (p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
|
|
response = "Aborted";
|
|
break;
|
|
}
|
|
|
|
response = Modem_Response (p);
|
|
if (!response)
|
|
continue;
|
|
if (Q_strncmp (response, "CONNECT", 7) == 0) {
|
|
disable ();
|
|
p->modemRang = true;
|
|
p->modemConnected = true;
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
enable ();
|
|
key_dest = save_key_dest;
|
|
key_count = 0;
|
|
m_return_onerror = false;
|
|
return 0;
|
|
}
|
|
if (Q_strncmp (response, "NO CARRIER", 10) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "NO DIALTONE", 11) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "NO DIAL TONE", 12) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "NO ANSWER", 9) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "BUSY", 4) == 0)
|
|
break;
|
|
if (Q_strncmp (response, "ERROR", 5) == 0)
|
|
break;
|
|
}
|
|
key_dest = save_key_dest;
|
|
key_count = 0;
|
|
if (m_return_onerror) {
|
|
key_dest = key_menu;
|
|
m_state = m_return_state;
|
|
m_return_onerror = false;
|
|
Q_strncpy (m_return_reason, response, 31);
|
|
}
|
|
return -1;
|
|
}
|
|
m_return_onerror = false;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
TTY_Disconnect (int handle)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
if (p->useModem && p->modemConnected)
|
|
Modem_Hangup (p);
|
|
}
|
|
|
|
|
|
qboolean
|
|
TTY_CheckForConnection (int handle)
|
|
{
|
|
ComPort *p;
|
|
|
|
p = handleToPort[handle];
|
|
|
|
CheckStatus (p);
|
|
|
|
if (p->useModem) {
|
|
if (!p->modemRang) {
|
|
if (!Modem_Response (p))
|
|
return false;
|
|
|
|
if (Q_strncmp (p->buffer, "RING", 4) == 0) {
|
|
Modem_Command (p, "ATA");
|
|
p->modemRang = true;
|
|
p->timestamp = net_time;
|
|
}
|
|
return false;
|
|
}
|
|
if (!p->modemConnected) {
|
|
if ((net_time - p->timestamp) > 35.0) {
|
|
Con_Printf ("Unable to establish modem connection\n");
|
|
p->modemRang = false;
|
|
return false;
|
|
}
|
|
|
|
if (!Modem_Response (p))
|
|
return false;
|
|
|
|
if (Q_strncmp (p->buffer, "CONNECT", 7) != 0)
|
|
return false;
|
|
|
|
disable ();
|
|
p->modemConnected = true;
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
enable ();
|
|
Con_Printf ("Modem Connect\n");
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
// direct connect case
|
|
if (EMPTY (p->inputQueue))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
qboolean
|
|
TTY_IsEnabled (int serialPortNumber)
|
|
{
|
|
return handleToPort[serialPortNumber]->enabled;
|
|
}
|
|
|
|
|
|
qboolean
|
|
TTY_IsModem (int serialPortNumber)
|
|
{
|
|
return handleToPort[serialPortNumber]->useModem;
|
|
}
|
|
|
|
|
|
qboolean
|
|
TTY_OutputQueueIsEmpty (int handle)
|
|
{
|
|
return EMPTY (handleToPort[handle]->outputQueue);
|
|
}
|
|
|
|
|
|
void
|
|
Com_f (void)
|
|
{
|
|
ComPort *p;
|
|
int portNumber;
|
|
int i;
|
|
int n;
|
|
|
|
// first, determine which port they're messing with
|
|
portNumber = Q_atoi (Cmd_Argv (0) + 3) - 1;
|
|
if (portNumber > 1)
|
|
return;
|
|
p = handleToPort[portNumber];
|
|
|
|
if (Cmd_Argc () == 1) {
|
|
Con_Printf ("Settings for COM%i\n", portNumber + 1);
|
|
Con_Printf ("enabled: %s\n", p->enabled ? "true" : "false");
|
|
Con_Printf ("uart: ");
|
|
if (p->uartType == UART_AUTO)
|
|
Con_Printf ("auto\n");
|
|
else if (p->uartType == UART_8250)
|
|
Con_Printf ("8250\n");
|
|
else
|
|
Con_Printf ("16550\n");
|
|
Con_Printf ("port: %x\n", p->uart);
|
|
Con_Printf ("irq: %i\n", p->irq);
|
|
Con_Printf ("baud: %i\n", 115200 / p->baudBits);
|
|
Con_Printf ("CTS: %s\n",
|
|
(p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored");
|
|
Con_Printf ("DSR: %s\n",
|
|
(p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored");
|
|
Con_Printf ("CD: %s\n",
|
|
(p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored");
|
|
if (p->useModem) {
|
|
Con_Printf ("type: Modem\n");
|
|
Con_Printf ("clear: %s\n", p->clear);
|
|
Con_Printf ("startup: %s\n", p->startup);
|
|
Con_Printf ("shutdown: %s\n", p->shutdown);
|
|
} else
|
|
Con_Printf ("type: Direct connect\n");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (Cmd_CheckParm ("disable")) {
|
|
if (p->enabled)
|
|
ComPort_Disable (p);
|
|
p->modemInitialized = false;
|
|
return;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("reset")) {
|
|
ComPort_Disable (p);
|
|
ResetComPortConfig (p);
|
|
return;
|
|
}
|
|
|
|
if ((i = Cmd_CheckParm ("port")) != 0) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change port\n");
|
|
return;
|
|
}
|
|
p->uart = Q_atoi (Cmd_Argv (i + 1));
|
|
}
|
|
|
|
if ((i = Cmd_CheckParm ("irq")) != 0) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change irq\n");
|
|
return;
|
|
}
|
|
p->irq = Q_atoi (Cmd_Argv (i + 1));
|
|
}
|
|
|
|
if ((i = Cmd_CheckParm ("baud")) != 0) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change baud\n");
|
|
return;
|
|
}
|
|
n = Q_atoi (Cmd_Argv (i + 1));
|
|
if (n == 0)
|
|
Con_Printf ("Invalid baud rate specified\n");
|
|
else
|
|
p->baudBits = 115200 / n;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("8250")) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change uart\n");
|
|
return;
|
|
}
|
|
p->uartType = UART_8250;
|
|
}
|
|
if (Cmd_CheckParm ("16550")) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change uart\n");
|
|
return;
|
|
}
|
|
p->uartType = UART_16550;
|
|
}
|
|
if (Cmd_CheckParm ("auto")) {
|
|
if (p->enabled) {
|
|
Con_Printf ("COM port must be disabled to change uart\n");
|
|
return;
|
|
}
|
|
p->uartType = UART_AUTO;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("pulse"))
|
|
p->dialType = 'P';
|
|
if (Cmd_CheckParm ("tone"))
|
|
p->dialType = 'T';
|
|
|
|
if (Cmd_CheckParm ("direct"))
|
|
p->useModem = false;
|
|
if (Cmd_CheckParm ("modem"))
|
|
p->useModem = true;
|
|
|
|
if ((i = Cmd_CheckParm ("clear")) != 0) {
|
|
Q_strncpy (p->clear, Cmd_Argv (i + 1), 16);
|
|
}
|
|
|
|
if ((i = Cmd_CheckParm ("startup")) != 0) {
|
|
Q_strncpy (p->startup, Cmd_Argv (i + 1), 32);
|
|
p->modemInitialized = false;
|
|
}
|
|
|
|
if ((i = Cmd_CheckParm ("shutdown")) != 0) {
|
|
Q_strncpy (p->shutdown, Cmd_Argv (i + 1), 16);
|
|
}
|
|
|
|
if (Cmd_CheckParm ("-cts")) {
|
|
p->modemStatusIgnore |= MSR_CTS;
|
|
p->modemStatus |= MSR_CTS;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("+cts")) {
|
|
p->modemStatusIgnore &= (~MSR_CTS);
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) |
|
|
p->modemStatusIgnore;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("-dsr")) {
|
|
p->modemStatusIgnore |= MSR_DSR;
|
|
p->modemStatus |= MSR_DSR;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("+dsr")) {
|
|
p->modemStatusIgnore &= (~MSR_DSR);
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) |
|
|
p->modemStatusIgnore;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("-cd")) {
|
|
p->modemStatusIgnore |= MSR_CD;
|
|
p->modemStatus |= MSR_CD;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("+cd")) {
|
|
p->modemStatusIgnore &= (~MSR_CD);
|
|
p->modemStatus =
|
|
(inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) |
|
|
p->modemStatusIgnore;
|
|
}
|
|
|
|
if (Cmd_CheckParm ("enable")) {
|
|
if (!p->enabled)
|
|
ComPort_Enable (p);
|
|
if (p->useModem && !p->modemInitialized)
|
|
Modem_Init (p);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
TTY_Init (void)
|
|
{
|
|
int n;
|
|
ComPort *p;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++) {
|
|
p = (ComPort *) Hunk_AllocName (sizeof (ComPort), "comport");
|
|
if (p == NULL)
|
|
Sys_Error ("Hunk alloc failed for com port\n");
|
|
p->next = portList;
|
|
portList = p;
|
|
handleToPort[n] = p;
|
|
p->portNumber = n;
|
|
p->dialType = 'T';
|
|
snprintf (p->name, sizeof (p->name), "com%u", n + 1);
|
|
Cmd_AddCommand (p->name, Com_f, "No Description");
|
|
ResetComPortConfig (p);
|
|
}
|
|
|
|
GetComPortConfig = TTY_GetComPortConfig;
|
|
SetComPortConfig = TTY_SetComPortConfig;
|
|
GetModemConfig = TTY_GetModemConfig;
|
|
SetModemConfig = TTY_SetModemConfig;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
TTY_Shutdown (void)
|
|
{
|
|
int n;
|
|
ComPort *p;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++) {
|
|
p = handleToPort[n];
|
|
if (p->enabled) {
|
|
while (p->modemConnected)
|
|
NET_Poll ();
|
|
ComPort_Disable (p);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
Modem_Command (ComPort * p, char *commandString)
|
|
{
|
|
byte b;
|
|
|
|
if (CheckStatus (p))
|
|
return -1;
|
|
|
|
disable ();
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
enable ();
|
|
p->bufferUsed = 0;
|
|
|
|
while (*commandString)
|
|
ENQUEUE (p->outputQueue, *commandString++);
|
|
ENQUEUE (p->outputQueue, '\r');
|
|
|
|
// get the transmit rolling
|
|
DEQUEUE (p->outputQueue, b);
|
|
outportb (p->uart, b);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *
|
|
Modem_Response (ComPort * p)
|
|
{
|
|
byte b;
|
|
|
|
if (CheckStatus (p))
|
|
return NULL;
|
|
|
|
while (!EMPTY (p->inputQueue)) {
|
|
DEQUEUE (p->inputQueue, b);
|
|
|
|
if (p->bufferUsed == (sizeof (p->buffer) - 1))
|
|
b = '\r';
|
|
|
|
if (b == '\r' && p->bufferUsed) {
|
|
p->buffer[p->bufferUsed] = 0;
|
|
Con_Printf ("%s\n", p->buffer);
|
|
SCR_UpdateScreen (cl.time);
|
|
p->bufferUsed = 0;
|
|
return p->buffer;
|
|
}
|
|
|
|
if (b < ' ' || b > 'z')
|
|
continue;
|
|
p->buffer[p->bufferUsed] = b;
|
|
p->bufferUsed++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void Modem_Hangup2 (ComPort * p);
|
|
static void Modem_Hangup3 (ComPort * p);
|
|
static void Modem_Hangup4 (ComPort * p);
|
|
|
|
static void
|
|
Modem_Hangup (ComPort * p)
|
|
{
|
|
Con_Printf ("Hanging up modem...\n");
|
|
disable ();
|
|
p->modemRang = false;
|
|
p->outputQueue.head = p->outputQueue.tail = 0;
|
|
p->inputQueue.head = p->inputQueue.tail = 0;
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER,
|
|
inportb (p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR);
|
|
enable ();
|
|
p->poll.procedure = Modem_Hangup2;
|
|
p->poll.arg = p;
|
|
SchedulePollProcedure (&p->poll, 1.5);
|
|
}
|
|
|
|
static void
|
|
Modem_Hangup2 (ComPort * p)
|
|
{
|
|
outportb (p->uart + MODEM_CONTROL_REGISTER,
|
|
inportb (p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
|
|
Modem_Command (p, "+++");
|
|
p->poll.procedure = Modem_Hangup3;
|
|
SchedulePollProcedure (&p->poll, 1.5);
|
|
}
|
|
|
|
static void
|
|
Modem_Hangup3 (ComPort * p)
|
|
{
|
|
Modem_Command (p, p->shutdown);
|
|
p->poll.procedure = Modem_Hangup4;
|
|
SchedulePollProcedure (&p->poll, 1.5);
|
|
}
|
|
|
|
static void
|
|
Modem_Hangup4 (ComPort * p)
|
|
{
|
|
Modem_Response (p);
|
|
Con_Printf ("Hangup complete\n");
|
|
p->modemConnected = false;
|
|
}
|