quakeforge-old/uquake/net_comx.c

1287 lines
29 KiB
C
Raw Normal View History

/*
Copyright (C) 1996-1997 Id Software, Inc.
Portions Copyright (C) 1999,2000 Nelson Rush.
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_comx.c
#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 cvar_t *config_com_port;
extern cvar_t *config_com_irq;
extern cvar_t *config_com_baud;
extern cvar_t *config_com_modem;
extern cvar_t *config_modem_dialtype;
extern cvar_t *config_modem_clear;
extern cvar_t *config_modem_init;
extern cvar_t *config_modem_hangup;
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;
}
Sys_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);
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 ();
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;
}